Commit c395453c by Mark Mitchell Committed by Mark Mitchell

cp-tree.h (struct language_function): Remove temp_name_counter.

	* cp-tree.h (struct language_function): Remove temp_name_counter.
	(temp_name_counter): Remove.
	(get_temp_name): Change prototype.
	(get_guard): New function.
	(get_guard_cond): Likewise.
	(set_guard): Likewise.
	* cvt.c (build_up_reference): Adjust call to get_temp_name.
	* decl.c (expand_static_init): Use get_guard and friends to
	implement guard variables.
	* decl2.c (get_temp_name): Assume that the variables created are
	always static.
	(get_sentry): Rename to ...
	(get_guard): ... this.  Implement new ABI guard	variables.
	(get_guard_bits): New function.
	(get_guard_cond): Likewise.
	(set_guard): Likewise.
	(start_static_initialization_or_destruction): Use them.
	(do_static_initialization): Replace sentry with guard throughout.
	(do_static_destruction): Likewise.
	* init.c (create_temporary_var): Add comment.

From-SVN: r34803
parent c1c8f8cc
2000-06-30 Mark Mitchell <mark@codesourcery.com>
* cp-tree.h (struct language_function): Remove temp_name_counter.
(temp_name_counter): Remove.
(get_temp_name): Change prototype.
(get_guard): New function.
(get_guard_cond): Likewise.
(set_guard): Likewise.
* cvt.c (build_up_reference): Adjust call to get_temp_name.
* decl.c (expand_static_init): Use get_guard and friends to
implement guard variables.
* decl2.c (get_temp_name): Assume that the variables created are
always static.
(get_sentry): Rename to ...
(get_guard): ... this. Implement new ABI guard variables.
(get_guard_bits): New function.
(get_guard_cond): Likewise.
(set_guard): Likewise.
(start_static_initialization_or_destruction): Use them.
(do_static_initialization): Replace sentry with guard throughout.
(do_static_destruction): Likewise.
* init.c (create_temporary_var): Add comment.
2000-06-29 Mark Mitchell <mark@codesourcery.com> 2000-06-29 Mark Mitchell <mark@codesourcery.com>
* cp-tree.h (flag_const_strings): Remove. * cp-tree.h (flag_const_strings): Remove.
......
...@@ -900,7 +900,6 @@ struct language_function ...@@ -900,7 +900,6 @@ struct language_function
int returns_value; int returns_value;
int returns_null; int returns_null;
int parms_stored; int parms_stored;
int temp_name_counter;
int in_function_try_handler; int in_function_try_handler;
int x_expanding_p; int x_expanding_p;
int name_declared; int name_declared;
...@@ -1004,11 +1003,6 @@ struct language_function ...@@ -1004,11 +1003,6 @@ struct language_function
#define vtbls_set_up_p cp_function_chain->vtbls_set_up_p #define vtbls_set_up_p cp_function_chain->vtbls_set_up_p
/* Used to help generate temporary names which are unique within
a function. Reset to 0 by start_function. */
#define temp_name_counter cp_function_chain->temp_name_counter
/* Non-zero if we should generate RTL for functions that we process. /* Non-zero if we should generate RTL for functions that we process.
When this is zero, we just accumulate tree structure, without When this is zero, we just accumulate tree structure, without
interacting with the back end. */ interacting with the back end. */
...@@ -4075,7 +4069,7 @@ extern tree constructor_name_full PARAMS ((tree)); ...@@ -4075,7 +4069,7 @@ extern tree constructor_name_full PARAMS ((tree));
extern tree constructor_name PARAMS ((tree)); extern tree constructor_name PARAMS ((tree));
extern void setup_vtbl_ptr PARAMS ((tree, tree)); extern void setup_vtbl_ptr PARAMS ((tree, tree));
extern void defer_fn PARAMS ((tree)); extern void defer_fn PARAMS ((tree));
extern tree get_temp_name PARAMS ((tree, int)); extern tree get_temp_name PARAMS ((tree));
extern void finish_anon_union PARAMS ((tree)); extern void finish_anon_union PARAMS ((tree));
extern tree finish_table PARAMS ((tree, tree, tree, int)); extern tree finish_table PARAMS ((tree, tree, tree, int));
extern void finish_builtin_type PARAMS ((tree, const char *, extern void finish_builtin_type PARAMS ((tree, const char *,
...@@ -4110,6 +4104,9 @@ extern tree handle_class_head PARAMS ((tree, tree, tree)); ...@@ -4110,6 +4104,9 @@ extern tree handle_class_head PARAMS ((tree, tree, tree));
extern tree lookup_arg_dependent PARAMS ((tree, tree, tree)); extern tree lookup_arg_dependent PARAMS ((tree, tree, tree));
extern void finish_static_data_member_decl PARAMS ((tree, tree, tree, int)); extern void finish_static_data_member_decl PARAMS ((tree, tree, tree, int));
extern tree build_artificial_parm PARAMS ((tree, tree)); extern tree build_artificial_parm PARAMS ((tree, tree));
extern tree get_guard PARAMS ((tree));
extern tree get_guard_cond PARAMS ((tree));
extern tree set_guard PARAMS ((tree));
/* in parse.y */ /* in parse.y */
extern void cp_parse_init PARAMS ((void)); extern void cp_parse_init PARAMS ((void));
......
...@@ -356,7 +356,7 @@ build_up_reference (type, arg, flags) ...@@ -356,7 +356,7 @@ build_up_reference (type, arg, flags)
/* Create a new temporary variable. */ /* Create a new temporary variable. */
tree targ = arg; tree targ = arg;
if (toplevel_bindings_p ()) if (toplevel_bindings_p ())
arg = get_temp_name (argtype, 1); arg = get_temp_name (argtype);
else else
{ {
arg = pushdecl (build_decl (VAR_DECL, NULL_TREE, argtype)); arg = pushdecl (build_decl (VAR_DECL, NULL_TREE, argtype));
......
...@@ -8572,19 +8572,19 @@ expand_static_init (decl, init) ...@@ -8572,19 +8572,19 @@ expand_static_init (decl, init)
else if (! toplevel_bindings_p ()) else if (! toplevel_bindings_p ())
{ {
/* Emit code to perform this initialization but once. */ /* Emit code to perform this initialization but once. */
tree temp;
tree if_stmt; tree if_stmt;
tree then_clause; tree then_clause;
tree assignment; tree assignment;
tree temp_init; tree guard;
tree guard_init;
/* Emit code to perform this initialization but once. This code /* Emit code to perform this initialization but once. This code
looks like: looks like:
static int temp = 0; static int guard = 0;
if (!temp) { if (!guard) {
// Do initialization. // Do initialization.
temp = 1; guard = 1;
// Register variable for destruction at end of program. // Register variable for destruction at end of program.
} }
...@@ -8602,14 +8602,13 @@ expand_static_init (decl, init) ...@@ -8602,14 +8602,13 @@ expand_static_init (decl, init)
In theory, this process should be thread-safe, too; multiple In theory, this process should be thread-safe, too; multiple
threads should not be able to initialize the variable more threads should not be able to initialize the variable more
than once. We don't yet attempt to ensure thread-safety. */ than once. We don't yet attempt to ensure thread-safety. */
temp = get_temp_name (integer_type_node, 1);
rest_of_decl_compilation (temp, NULL_PTR, 0, 0); /* Create the guard variable. */
guard = get_guard (decl);
/* Begin the conditional initialization. */ /* Begin the conditional initialization. */
if_stmt = begin_if_stmt (); if_stmt = begin_if_stmt ();
finish_if_stmt_cond (cp_build_binary_op (EQ_EXPR, temp, finish_if_stmt_cond (get_guard_cond (guard), if_stmt);
integer_zero_node),
if_stmt);
then_clause = begin_compound_stmt (/*has_no_scope=*/0); then_clause = begin_compound_stmt (/*has_no_scope=*/0);
/* Do the initialization itself. */ /* Do the initialization itself. */
...@@ -8631,16 +8630,16 @@ expand_static_init (decl, init) ...@@ -8631,16 +8630,16 @@ expand_static_init (decl, init)
the assignment to TEMP into a single expression, ensuring the assignment to TEMP into a single expression, ensuring
that when we call finish_expr_stmt the cleanups will not be that when we call finish_expr_stmt the cleanups will not be
run until after TEMP is set to 1. */ run until after TEMP is set to 1. */
temp_init = build_modify_expr (temp, NOP_EXPR, integer_one_node); guard_init = set_guard (guard);
if (assignment) if (assignment)
{ {
assignment = tree_cons (NULL_TREE, assignment, assignment = tree_cons (NULL_TREE, assignment,
build_tree_list (NULL_TREE, build_tree_list (NULL_TREE,
temp_init)); guard_init));
assignment = build_compound_expr (assignment); assignment = build_compound_expr (assignment);
} }
else else
assignment = temp_init; assignment = guard_init;
finish_expr_stmt (assignment); finish_expr_stmt (assignment);
/* Use atexit to register a function for destroying this static /* Use atexit to register a function for destroying this static
......
...@@ -62,7 +62,6 @@ typedef struct priority_info_s { ...@@ -62,7 +62,6 @@ typedef struct priority_info_s {
int destructions_p; int destructions_p;
} *priority_info; } *priority_info;
static tree get_sentry PARAMS ((tree));
static void mark_vtable_entries PARAMS ((tree)); static void mark_vtable_entries PARAMS ((tree));
static void grok_function_init PARAMS ((tree, tree)); static void grok_function_init PARAMS ((tree, tree));
static int finish_vtable_vardecl PARAMS ((tree *, void *)); static int finish_vtable_vardecl PARAMS ((tree *, void *));
...@@ -95,6 +94,7 @@ static void write_out_vars PARAMS ((tree)); ...@@ -95,6 +94,7 @@ static void write_out_vars PARAMS ((tree));
static void import_export_class PARAMS ((tree)); static void import_export_class PARAMS ((tree));
static tree key_method PARAMS ((tree)); static tree key_method PARAMS ((tree));
static int compare_options PARAMS ((const PTR, const PTR)); static int compare_options PARAMS ((const PTR, const PTR));
static tree get_guard_bits PARAMS ((tree));
extern int current_class_depth; extern int current_class_depth;
...@@ -2098,32 +2098,24 @@ defer_fn (fn) ...@@ -2098,32 +2098,24 @@ defer_fn (fn)
/* Hand off a unique name which can be used for variable we don't really /* Hand off a unique name which can be used for variable we don't really
want to know about anyway, for example, the anonymous variables which want to know about anyway, for example, the anonymous variables which
are needed to make references work. Declare this thing so we can use it. are needed to make references work. Declare this thing so we can use it.
The variable created will be of type TYPE. The variable created will be of type TYPE, and will have internal
linkage. */
STATICP is nonzero if this variable should be static. */
tree tree
get_temp_name (type, staticp) get_temp_name (type)
tree type; tree type;
int staticp;
{ {
char buf[sizeof (AUTO_TEMP_FORMAT) + 20]; char buf[sizeof (AUTO_TEMP_FORMAT) + 20];
tree decl; tree decl;
int toplev = toplevel_bindings_p (); int toplev = toplevel_bindings_p ();
if (toplev || staticp)
{
sprintf (buf, AUTO_TEMP_FORMAT, global_temp_name_counter++); sprintf (buf, AUTO_TEMP_FORMAT, global_temp_name_counter++);
decl = pushdecl_top_level (build_decl (VAR_DECL, get_identifier (buf), type)); decl = build_decl (VAR_DECL, get_identifier (buf), type);
}
else
{
sprintf (buf, AUTO_TEMP_FORMAT, temp_name_counter++);
decl = pushdecl (build_decl (VAR_DECL, get_identifier (buf), type));
}
TREE_USED (decl) = 1;
TREE_STATIC (decl) = staticp;
DECL_ARTIFICIAL (decl) = 1; DECL_ARTIFICIAL (decl) = 1;
TREE_USED (decl) = 1;
TREE_STATIC (decl) = 1;
decl = pushdecl_top_level (decl);
/* If this is a local variable, then lay out its rtl now. /* If this is a local variable, then lay out its rtl now.
Otherwise, callers of this function are responsible for dealing Otherwise, callers of this function are responsible for dealing
...@@ -2866,37 +2858,111 @@ build_cleanup (decl) ...@@ -2866,37 +2858,111 @@ build_cleanup (decl)
return temp; return temp;
} }
/* Returns the initialization guard variable for the non-local /* Returns the initialization guard variable for the variable DECL,
variable DECL. */ which has static storage duration. */
static tree tree
get_sentry (decl) get_guard (decl)
tree decl; tree decl;
{ {
tree sname; tree sname;
tree sentry; tree guard;
/* For a local variable, under the old ABI, we do not try to get a
unique mangled name for the DECL. */
if (!flag_new_abi && !DECL_NAMESPACE_SCOPE_P (decl))
{
guard = get_temp_name (integer_type_node);
rest_of_decl_compilation (guard, NULL_PTR, 0, 0);
}
if (!flag_new_abi) if (!flag_new_abi)
/* For struct X foo __attribute__((weak)), there is a counter
__snfoo. Since base is already an assembler name, sname should
be globally unique */
sname = get_id_2 ("__sn", DECL_ASSEMBLER_NAME (decl)); sname = get_id_2 ("__sn", DECL_ASSEMBLER_NAME (decl));
else else
sname = mangle_guard_variable (decl); sname = mangle_guard_variable (decl);
/* For struct X foo __attribute__((weak)), there is a counter guard = IDENTIFIER_GLOBAL_VALUE (sname);
__snfoo. Since base is already an assembler name, sname should if (! guard)
be globally unique */ {
sentry = IDENTIFIER_GLOBAL_VALUE (sname); tree guard_type;
if (! sentry)
{ /* Under the new ABI, we use a type that is big enough to
sentry = build_decl (VAR_DECL, sname, integer_type_node); contain a mutex as well as an integer counter. */
TREE_PUBLIC (sentry) = 1; if (flag_new_abi)
DECL_ARTIFICIAL (sentry) = 1; guard_type = long_long_integer_type_node;
TREE_STATIC (sentry) = 1; else
TREE_USED (sentry) = 1; guard_type = integer_type_node;
DECL_COMMON (sentry) = 1;
pushdecl_top_level (sentry); guard = build_decl (VAR_DECL, sname, guard_type);
cp_finish_decl (sentry, NULL_TREE, NULL_TREE, 0); TREE_PUBLIC (guard) = 1;
} DECL_ARTIFICIAL (guard) = 1;
return sentry; TREE_STATIC (guard) = 1;
TREE_USED (guard) = 1;
DECL_COMMON (guard) = 1;
pushdecl_top_level (guard);
cp_finish_decl (guard, NULL_TREE, NULL_TREE, 0);
}
return guard;
}
/* Return those bits of the GUARD variable that should be set when the
guarded entity is actually initialized. */
static tree
get_guard_bits (guard)
tree guard;
{
if (!flag_new_abi)
return guard;
/* Under the new ABI, we only set the first byte of the guard,
in order to leave room for a mutex in the high-order bits. */
guard = build1 (ADDR_EXPR,
build_pointer_type (TREE_TYPE (guard)),
guard);
guard = build1 (NOP_EXPR,
build_pointer_type (char_type_node),
guard);
guard = build1 (INDIRECT_REF, char_type_node, guard);
return guard;
}
/* Return an expression which determines whether or not the GUARD
variable has already been initialized. */
tree
get_guard_cond (guard)
tree guard;
{
tree guard_value;
/* Check to see if the GUARD is zero. */
guard = get_guard_bits (guard);
guard_value = integer_zero_node;
if (!same_type_p (TREE_TYPE (guard_value), TREE_TYPE (guard)))
guard_value = convert (TREE_TYPE (guard), guard_value);
return cp_build_binary_op (EQ_EXPR, guard, guard_value);
}
/* Return an expression which sets the GUARD variable, indicating that
the variable being guarded has been initialized. */
tree
set_guard (guard)
tree guard;
{
tree guard_init;
/* Set the GUARD to one. */
guard = get_guard_bits (guard);
guard_init = integer_one_node;
if (!same_type_p (TREE_TYPE (guard_init), TREE_TYPE (guard)))
guard_init = convert (TREE_TYPE (guard), guard_init);
return build_modify_expr (guard, NOP_EXPR, guard_init);
} }
/* Start the process of running a particular set of global constructors /* Start the process of running a particular set of global constructors
...@@ -3201,9 +3267,10 @@ start_static_initialization_or_destruction (decl, initp) ...@@ -3201,9 +3267,10 @@ start_static_initialization_or_destruction (decl, initp)
tree decl; tree decl;
int initp; int initp;
{ {
tree sentry_if_stmt = NULL_TREE; tree guard_if_stmt = NULL_TREE;
int priority; int priority;
tree cond; tree cond;
tree guard;
tree init_cond; tree init_cond;
priority_info pi; priority_info pi;
...@@ -3247,7 +3314,7 @@ start_static_initialization_or_destruction (decl, initp) ...@@ -3247,7 +3314,7 @@ start_static_initialization_or_destruction (decl, initp)
/* Conditionalize this initialization on being in the right priority /* Conditionalize this initialization on being in the right priority
and being initializing/finalizing appropriately. */ and being initializing/finalizing appropriately. */
sentry_if_stmt = begin_if_stmt (); guard_if_stmt = begin_if_stmt ();
cond = cp_build_binary_op (EQ_EXPR, cond = cp_build_binary_op (EQ_EXPR,
priority_decl, priority_decl,
build_int_2 (priority, 0)); build_int_2 (priority, 0));
...@@ -3257,7 +3324,9 @@ start_static_initialization_or_destruction (decl, initp) ...@@ -3257,7 +3324,9 @@ start_static_initialization_or_destruction (decl, initp)
init_cond); init_cond);
cond = cp_build_binary_op (TRUTH_ANDIF_EXPR, cond, init_cond); cond = cp_build_binary_op (TRUTH_ANDIF_EXPR, cond, init_cond);
/* We need a sentry if this is an object with external linkage that /* Assume we don't need a guard. */
guard = NULL_TREE;
/* We need a guard if this is an object with external linkage that
might be initialized in more than one place. (For example, a might be initialized in more than one place. (For example, a
static data member of a template, when the data member requires static data member of a template, when the data member requires
construction.) */ construction.) */
...@@ -3265,47 +3334,60 @@ start_static_initialization_or_destruction (decl, initp) ...@@ -3265,47 +3334,60 @@ start_static_initialization_or_destruction (decl, initp)
|| DECL_ONE_ONLY (decl) || DECL_ONE_ONLY (decl)
|| DECL_WEAK (decl))) || DECL_WEAK (decl)))
{ {
tree sentry; tree guard_cond;
tree sentry_cond;
sentry = get_sentry (decl); guard = get_guard (decl);
/* We do initializations only if the SENTRY is zero, i.e., if we /* When using __cxa_atexit, we just check the GUARD as we would
are the first to initialize the variable. We do destructions for a local static. */
only if the SENTRY is one, i.e., if we are the last to if (flag_use_cxa_atexit)
destroy the variable. */ {
if (initp) /* When using __cxa_atexit, we never try to destroy
sentry_cond anything from a static destructor. */
my_friendly_assert (initp, 20000629);
guard_cond = get_guard_cond (guard);
}
/* Under the old ABI, e do initializations only if the GUARD is
zero, i.e., if we are the first to initialize the variable.
We do destructions only if the GUARD is one, i.e., if we are
the last to destroy the variable. */
else if (initp)
guard_cond
= cp_build_binary_op (EQ_EXPR, = cp_build_binary_op (EQ_EXPR,
build_unary_op (PREINCREMENT_EXPR, build_unary_op (PREINCREMENT_EXPR,
sentry, guard,
/*noconvert=*/1), /*noconvert=*/1),
integer_one_node); integer_one_node);
else else
sentry_cond guard_cond
= cp_build_binary_op (EQ_EXPR, = cp_build_binary_op (EQ_EXPR,
build_unary_op (PREDECREMENT_EXPR, build_unary_op (PREDECREMENT_EXPR,
sentry, guard,
/*noconvert=*/1), /*noconvert=*/1),
integer_zero_node); integer_zero_node);
cond = cp_build_binary_op (TRUTH_ANDIF_EXPR, cond, sentry_cond); cond = cp_build_binary_op (TRUTH_ANDIF_EXPR, cond, guard_cond);
} }
finish_if_stmt_cond (cond, sentry_if_stmt); finish_if_stmt_cond (cond, guard_if_stmt);
/* Under the new ABI, we have not already set the GUARD, so we must
do so now. */
if (guard && initp && flag_new_abi)
finish_expr_stmt (set_guard (guard));
return sentry_if_stmt; return guard_if_stmt;
} }
/* We've just finished generating code to do an initialization or /* We've just finished generating code to do an initialization or
finalization. SENTRY_IF_STMT is the if-statement we used to guard finalization. GUARD_IF_STMT is the if-statement we used to guard
the initialization. */ the initialization. */
static void static void
finish_static_initialization_or_destruction (sentry_if_stmt) finish_static_initialization_or_destruction (guard_if_stmt)
tree sentry_if_stmt; tree guard_if_stmt;
{ {
finish_then_clause (sentry_if_stmt); finish_then_clause (guard_if_stmt);
finish_if_stmt (); finish_if_stmt ();
/* Now that we're done with DECL we don't need to pretend to be a /* Now that we're done with DECL we don't need to pretend to be a
...@@ -3316,7 +3398,7 @@ finish_static_initialization_or_destruction (sentry_if_stmt) ...@@ -3316,7 +3398,7 @@ finish_static_initialization_or_destruction (sentry_if_stmt)
/* Generate code to do the static initialization of DECL. The /* Generate code to do the static initialization of DECL. The
initialization is INIT. If DECL may be initialized more than once initialization is INIT. If DECL may be initialized more than once
in different object files, SENTRY is the guard variable to in different object files, GUARD is the guard variable to
check. PRIORITY is the priority for the initialization. */ check. PRIORITY is the priority for the initialization. */
static void static void
...@@ -3325,10 +3407,10 @@ do_static_initialization (decl, init) ...@@ -3325,10 +3407,10 @@ do_static_initialization (decl, init)
tree init; tree init;
{ {
tree expr; tree expr;
tree sentry_if_stmt; tree guard_if_stmt;
/* Set up for the initialization. */ /* Set up for the initialization. */
sentry_if_stmt guard_if_stmt
= start_static_initialization_or_destruction (decl, = start_static_initialization_or_destruction (decl,
/*initp=*/1); /*initp=*/1);
...@@ -3353,11 +3435,11 @@ do_static_initialization (decl, init) ...@@ -3353,11 +3435,11 @@ do_static_initialization (decl, init)
register_dtor_fn (decl); register_dtor_fn (decl);
/* Finsh up. */ /* Finsh up. */
finish_static_initialization_or_destruction (sentry_if_stmt); finish_static_initialization_or_destruction (guard_if_stmt);
} }
/* Generate code to do the static destruction of DECL. If DECL may be /* Generate code to do the static destruction of DECL. If DECL may be
initialized more than once in different object files, SENTRY is the initialized more than once in different object files, GUARD is the
guard variable to check. PRIORITY is the priority for the guard variable to check. PRIORITY is the priority for the
destruction. */ destruction. */
...@@ -3365,7 +3447,7 @@ static void ...@@ -3365,7 +3447,7 @@ static void
do_static_destruction (decl) do_static_destruction (decl)
tree decl; tree decl;
{ {
tree sentry_if_stmt; tree guard_if_stmt;
/* If we're using __cxa_atexit, then destructors are registered /* If we're using __cxa_atexit, then destructors are registered
immediately after objects are initialized. */ immediately after objects are initialized. */
...@@ -3376,10 +3458,10 @@ do_static_destruction (decl) ...@@ -3376,10 +3458,10 @@ do_static_destruction (decl)
return; return;
/* Actually do the destruction. */ /* Actually do the destruction. */
sentry_if_stmt = start_static_initialization_or_destruction (decl, guard_if_stmt = start_static_initialization_or_destruction (decl,
/*initp=*/0); /*initp=*/0);
finish_expr_stmt (build_cleanup (decl)); finish_expr_stmt (build_cleanup (decl));
finish_static_initialization_or_destruction (sentry_if_stmt); finish_static_initialization_or_destruction (guard_if_stmt);
} }
/* VARS is a list of variables with static storage duration which may /* VARS is a list of variables with static storage duration which may
......
...@@ -2729,6 +2729,8 @@ build_vec_delete_1 (base, maxindex, type, auto_delete_vec, use_global_delete) ...@@ -2729,6 +2729,8 @@ build_vec_delete_1 (base, maxindex, type, auto_delete_vec, use_global_delete)
return cp_convert (void_type_node, body); return cp_convert (void_type_node, body);
} }
/* Create an unnamed variable of the indicated TYPE. */
tree tree
create_temporary_var (type) create_temporary_var (type)
tree type; tree type;
......
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