Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
Zwinkau
libfirm
Commits
99c702d3
Commit
99c702d3
authored
Jun 12, 2004
by
Florian Liekweg
Browse files
Kommentare eingef"ugt --flo
[r3065]
parent
e7eac363
Changes
2
Hide whitespace changes
Inline
Side-by-side
ir/ana/cgana.c
View file @
99c702d3
...
@@ -110,8 +110,8 @@ void collect_impls(entity *method, eset *set, int *size, bool *open) {
...
@@ -110,8 +110,8 @@ void collect_impls(entity *method, eset *set, int *size, bool *open) {
}
else
{
}
else
{
assert
(
get_entity_irg
(
method
)
!=
NULL
);
assert
(
get_entity_irg
(
method
)
!=
NULL
);
if
(
!
eset_contains
(
set
,
method
))
{
if
(
!
eset_contains
(
set
,
method
))
{
eset_insert
(
set
,
method
);
eset_insert
(
set
,
method
);
++
(
*
size
);
++
(
*
size
);
}
}
}
}
}
}
...
@@ -124,8 +124,8 @@ void collect_impls(entity *method, eset *set, int *size, bool *open) {
...
@@ -124,8 +124,8 @@ void collect_impls(entity *method, eset *set, int *size, bool *open) {
}
else
{
}
else
{
assert
(
get_entity_irg
(
impl_ent
)
!=
NULL
);
assert
(
get_entity_irg
(
impl_ent
)
!=
NULL
);
if
(
!
eset_contains
(
set
,
impl_ent
))
{
if
(
!
eset_contains
(
set
,
impl_ent
))
{
eset_insert
(
set
,
impl_ent
);
eset_insert
(
set
,
impl_ent
);
++
(
*
size
);
++
(
*
size
);
}
}
}
}
}
}
...
@@ -196,22 +196,22 @@ static void sel_methods_walker(ir_node * node, pmap * ldname_map) {
...
@@ -196,22 +196,22 @@ static void sel_methods_walker(ir_node * node, pmap * ldname_map) {
if
(
get_SymConst_kind
(
node
)
==
linkage_ptr_info
)
{
if
(
get_SymConst_kind
(
node
)
==
linkage_ptr_info
)
{
pmap_entry
*
entry
=
pmap_find
(
ldname_map
,
(
void
*
)
get_SymConst_ptrinfo
(
node
));
pmap_entry
*
entry
=
pmap_find
(
ldname_map
,
(
void
*
)
get_SymConst_ptrinfo
(
node
));
if
(
entry
!=
NULL
)
{
/* Method is declared in the compiled code */
if
(
entry
!=
NULL
)
{
/* Method is declared in the compiled code */
entity
*
ent
=
entry
->
value
;
entity
*
ent
=
entry
->
value
;
if
(
get_opt_normalize
()
&&
(
get_entity_visibility
(
ent
)
!=
visibility_external_allocated
))
{
/* Meth. is defined */
if
(
get_opt_normalize
()
&&
(
get_entity_visibility
(
ent
)
!=
visibility_external_allocated
))
{
/* Meth. is defined */
ir_node
*
new_node
;
ir_node
*
new_node
;
assert
(
get_entity_irg
(
ent
));
assert
(
get_entity_irg
(
ent
));
set_irg_current_block
(
current_ir_graph
,
get_nodes_Block
(
node
));
set_irg_current_block
(
current_ir_graph
,
get_nodes_Block
(
node
));
new_node
=
new_d_Const
(
get_irn_dbg_info
(
node
),
new_node
=
new_d_Const
(
get_irn_dbg_info
(
node
),
mode_P_mach
,
new_tarval_from_entity
(
ent
,
mode_P_mach
));
DBG_OPT_NORMALIZE
;
mode_P_mach
,
new_tarval_from_entity
(
ent
,
mode_P_mach
));
DBG_OPT_NORMALIZE
;
exchange
(
node
,
new_node
);
exchange
(
node
,
new_node
);
}
}
}
}
}
}
}
else
if
(
intern_get_irn_op
(
node
)
==
op_Sel
&&
}
else
if
(
intern_get_irn_op
(
node
)
==
op_Sel
&&
is_method_type
(
get_entity_type
(
get_Sel_entity
(
node
))))
{
is_method_type
(
get_entity_type
(
get_Sel_entity
(
node
))))
{
entity
*
ent
=
get_Sel_entity
(
node
);
entity
*
ent
=
get_Sel_entity
(
node
);
if
(
get_opt_optimize
()
&&
get_opt_dyn_meth_dispatch
()
&&
if
(
get_opt_optimize
()
&&
get_opt_dyn_meth_dispatch
()
&&
(
intern_get_irn_op
(
skip_Proj
(
get_Sel_ptr
(
node
)))
==
op_Alloc
))
{
(
intern_get_irn_op
(
skip_Proj
(
get_Sel_ptr
(
node
)))
==
op_Alloc
))
{
ir_node
*
new_node
;
ir_node
*
new_node
;
entity
*
called_ent
;
entity
*
called_ent
;
/* We know which method will be called, no dispatch necessary. */
/* We know which method will be called, no dispatch necessary. */
...
@@ -219,7 +219,7 @@ static void sel_methods_walker(ir_node * node, pmap * ldname_map) {
...
@@ -219,7 +219,7 @@ static void sel_methods_walker(ir_node * node, pmap * ldname_map) {
assert(get_entity_peculiarity(ent) != peculiarity_description);
assert(get_entity_peculiarity(ent) != peculiarity_description);
set_irg_current_block(current_ir_graph, get_nodes_Block(node));
set_irg_current_block(current_ir_graph, get_nodes_Block(node));
/* @@@ Is this correct?? Alloc could reference a subtype of the owner
/* @@@ Is this correct?? Alloc could reference a subtype of the owner
of Sel that overwrites the method referenced in Sel. */
of Sel that overwrites the method referenced in Sel. */
/* @@@ Yes, this is wrong. GL, 10.3.04 */
/* @@@ Yes, this is wrong. GL, 10.3.04 */
new_node = copy_const_value(get_atomic_ent_value(ent)); DBG_OPT_POLY_ALLOC;
new_node = copy_const_value(get_atomic_ent_value(ent)); DBG_OPT_POLY_ALLOC;
#else
#else
...
@@ -233,55 +233,55 @@ static void sel_methods_walker(ir_node * node, pmap * ldname_map) {
...
@@ -233,55 +233,55 @@ static void sel_methods_walker(ir_node * node, pmap * ldname_map) {
}
else
{
}
else
{
assert
(
get_entity_peculiarity
(
ent
)
!=
peculiarity_inherited
);
assert
(
get_entity_peculiarity
(
ent
)
!=
peculiarity_inherited
);
if
(
!
eset_contains
(
entities
,
ent
))
{
if
(
!
eset_contains
(
entities
,
ent
))
{
/* Entity noch nicht behandelt. Alle (intern oder extern)
/* Entity noch nicht behandelt. Alle (intern oder extern)
* implementierten Methoden suchen, die diese Entity
* implementierten Methoden suchen, die diese Entity
* berschreiben. Die Menge an entity.link speichern. */
* berschreiben. Die Menge an entity.link speichern. */
set_entity_link
(
ent
,
get_impl_methods
(
ent
));
set_entity_link
(
ent
,
get_impl_methods
(
ent
));
eset_insert
(
entities
,
ent
);
eset_insert
(
entities
,
ent
);
}
}
if
(
get_entity_link
(
ent
)
==
NULL
)
{
if
(
get_entity_link
(
ent
)
==
NULL
)
{
/* Die Sel-Operation kann nie einen Zeiger auf eine aufrufbare
/* Die Sel-Operation kann nie einen Zeiger auf eine aufrufbare
* Methode zurckgeben. Damit ist sie insbesondere nicht
* Methode zurckgeben. Damit ist sie insbesondere nicht
* ausfhrbar und nicht erreichbar. */
* ausfhrbar und nicht erreichbar. */
/* Gib eine Warnung aus wenn die Entitaet eine Beschreibung ist
/* Gib eine Warnung aus wenn die Entitaet eine Beschreibung ist
fuer die es keine Implementierung gibt. */
fuer die es keine Implementierung gibt. */
if
(
get_entity_peculiarity
(
ent
)
==
peculiarity_description
)
{
if
(
get_entity_peculiarity
(
ent
)
==
peculiarity_description
)
{
/* @@@ GL Methode um Fehler anzuzeigen aufrufen! */
/* @@@ GL Methode um Fehler anzuzeigen aufrufen! */
printf
(
"WARNING: Calling method description %s
\n
in method %s
\n
of class %s
\n
which has "
printf
(
"WARNING: Calling method description %s
\n
in method %s
\n
of class %s
\n
which has "
"no implementation!
\n
"
,
get_entity_name
(
ent
),
"no implementation!
\n
"
,
get_entity_name
(
ent
),
get_entity_name
(
get_irg_ent
(
current_ir_graph
)),
get_entity_name
(
get_irg_ent
(
current_ir_graph
)),
get_type_name
(
get_entity_owner
(
get_irg_ent
(
current_ir_graph
))));
get_type_name
(
get_entity_owner
(
get_irg_ent
(
current_ir_graph
))));
printf
(
"This happens when compiling a Java Interface that's never really used, i.e., "
printf
(
"This happens when compiling a Java Interface that's never really used, i.e., "
"no class implementing the interface is ever used.
\n
"
);
"no class implementing the interface is ever used.
\n
"
);
}
else
{
}
else
{
exchange
(
node
,
new_Bad
());
exchange
(
node
,
new_Bad
());
}
}
}
else
{
}
else
{
entity
**
arr
=
get_entity_link
(
ent
);
entity
**
arr
=
get_entity_link
(
ent
);
#if 0
#if 0
int i;
int i;
printf("\nCall site "); DDMN(node);
printf("\nCall site "); DDMN(node);
printf(" in "); DDME(get_irg_ent(current_ir_graph));
printf(" in "); DDME(get_irg_ent(current_ir_graph));
printf(" can call:\n");
printf(" can call:\n");
for (i = 0; i < ARR_LEN(arr); i++) {
for (i = 0; i < ARR_LEN(arr); i++) {
printf(" - "); DDME(arr[i]);
printf(" - "); DDME(arr[i]);
printf(" with owner "); DDMT(get_entity_owner(arr[i]));
printf(" with owner "); DDMT(get_entity_owner(arr[i]));
}
}
printf("\n");
printf("\n");
#endif
#endif
if
(
get_opt_optimize
()
&&
get_opt_dyn_meth_dispatch
()
&&
if
(
get_opt_optimize
()
&&
get_opt_dyn_meth_dispatch
()
&&
(
ARR_LEN
(
arr
)
==
1
&&
arr
[
0
]
!=
NULL
))
{
(
ARR_LEN
(
arr
)
==
1
&&
arr
[
0
]
!=
NULL
))
{
ir_node
*
new_node
;
ir_node
*
new_node
;
/* Die Sel-Operation kann immer nur einen Wert auf eine
/* Die Sel-Operation kann immer nur einen Wert auf eine
* interne Methode zurckgeben. Wir knnen daher die
* interne Methode zurckgeben. Wir knnen daher die
* Sel-Operation durch eine Const- bzw. SymConst-Operation
* Sel-Operation durch eine Const- bzw. SymConst-Operation
* ersetzen. */
* ersetzen. */
set_irg_current_block
(
current_ir_graph
,
get_nodes_Block
(
node
));
set_irg_current_block
(
current_ir_graph
,
get_nodes_Block
(
node
));
new_node
=
copy_const_value
(
get_atomic_ent_value
(
arr
[
0
]));
DBG_OPT_POLY
;
new_node
=
copy_const_value
(
get_atomic_ent_value
(
arr
[
0
]));
DBG_OPT_POLY
;
exchange
(
node
,
new_node
);
exchange
(
node
,
new_node
);
}
}
}
}
}
}
}
}
...
@@ -445,9 +445,9 @@ static void callee_ana_node(ir_node * node, eset * methods) {
...
@@ -445,9 +445,9 @@ static void callee_ana_node(ir_node * node, eset * methods) {
for
(
i
=
get_Sel_n_methods
(
node
)
-
1
;
i
>=
0
;
--
i
)
{
for
(
i
=
get_Sel_n_methods
(
node
)
-
1
;
i
>=
0
;
--
i
)
{
entity
*
ent
=
get_Sel_method
(
node
,
i
);
entity
*
ent
=
get_Sel_method
(
node
,
i
);
if
(
ent
)
{
if
(
ent
)
{
eset_insert
(
methods
,
ent
);
eset_insert
(
methods
,
ent
);
}
else
{
}
else
{
eset_insert
(
methods
,
MARK
);
eset_insert
(
methods
,
MARK
);
}
}
}
}
break
;
break
;
...
...
ir/ana/rta.c
View file @
99c702d3
...
@@ -54,6 +54,9 @@ static eset *_dead_graphs = NULL;
...
@@ -54,6 +54,9 @@ static eset *_dead_graphs = NULL;
/* now the meat */
/* now the meat */
/**
Enter all method and field accesses and all class allocations into our tables.
*/
static
void
rta_act
(
ir_node
*
node
,
void
*
env
)
static
void
rta_act
(
ir_node
*
node
,
void
*
env
)
{
{
opcode
op
=
get_irn_opcode
(
node
);
opcode
op
=
get_irn_opcode
(
node
);
...
@@ -62,12 +65,15 @@ static void rta_act (ir_node *node, void *env)
...
@@ -62,12 +65,15 @@ static void rta_act (ir_node *node, void *env)
entity
*
ent
=
NULL
;
entity
*
ent
=
NULL
;
ir_node
*
ptr
=
get_Call_ptr
(
node
);
ir_node
*
ptr
=
get_Call_ptr
(
node
);
// TODO: test: ptr.op == Const
if
(
iro_Sel
==
get_irn_opcode
(
ptr
))
{
if
(
iro_Sel
==
get_irn_opcode
(
ptr
))
{
ent
=
get_Sel_entity
(
ptr
);
ent
=
get_Sel_entity
(
ptr
);
}
else
if
(
iro_Const
==
get_irn_opcode
(
ptr
))
{
ent
=
get_tarval_entity
(
get_Const_tarval
(
ptr
));
}
}
assert
(
ent
);
if
(
ent
)
{
if
(
ent
)
{
eset_insert
(
_called_methods
,
ent
);
eset_insert
(
_called_methods
,
ent
);
}
}
...
@@ -93,10 +99,14 @@ static void rta_act (ir_node *node, void *env)
...
@@ -93,10 +99,14 @@ static void rta_act (ir_node *node, void *env)
}
}
}
else
if
(
iro_Alloc
==
op
)
{
}
else
if
(
iro_Alloc
==
op
)
{
type
*
type
=
get_Alloc_type
(
node
);
type
*
type
=
get_Alloc_type
(
node
);
eset_insert
(
_live_classes
,
type
);
eset_insert
(
_live_classes
,
type
);
}
}
}
}
/**
Traverse the given graph to collect method and field accesses and object allocations.
*/
static
void
rta_fill_graph
(
ir_graph
*
graph
)
static
void
rta_fill_graph
(
ir_graph
*
graph
)
{
{
if
(
NULL
!=
graph
)
{
if
(
NULL
!=
graph
)
{
...
@@ -106,6 +116,9 @@ static void rta_fill_graph (ir_graph* graph)
...
@@ -106,6 +116,9 @@ static void rta_fill_graph (ir_graph* graph)
}
}
}
}
/**
Traverse all graphs to collect method and field accesses and object allocations.
*/
static
void
rta_fill_all
()
static
void
rta_fill_all
()
{
{
int
i
;
int
i
;
...
@@ -118,7 +131,11 @@ static void rta_fill_all ()
...
@@ -118,7 +131,11 @@ static void rta_fill_all ()
interprocedural_view
=
old_ip_view
;
interprocedural_view
=
old_ip_view
;
}
}
static
int
is_call_target
(
entity
*
method
)
/**
Find out whether the given method could be the target of a
polymorphic call.
*/
static
int
is_call_target
(
const
entity
*
method
)
{
{
int
is_target
=
FALSE
;
int
is_target
=
FALSE
;
int
i
;
int
i
;
...
@@ -127,30 +144,33 @@ static int is_call_target (entity *method)
...
@@ -127,30 +144,33 @@ static int is_call_target (entity *method)
/* The method could be the target of a polymorphic call if it is
/* The method could be the target of a polymorphic call if it is
called or if it overrides a method that is called. */
called or if it overrides a method that is called. */
if
(
eset_contains
(
_called_methods
,
method
))
{
if
(
eset_contains
(
_called_methods
,
(
entity
*
)
method
))
{
return
(
TRUE
);
return
(
TRUE
);
}
}
n_over
=
get_entity_n_overwrittenby
(
method
);
/* not called? check methods in superclass(es) */
n_over
=
get_entity_n_overwrittenby
((
entity
*
)
method
);
for
(
i
=
0
;
!
is_target
&&
(
i
<
n_over
);
i
++
)
{
for
(
i
=
0
;
!
is_target
&&
(
i
<
n_over
);
i
++
)
{
entity
*
over
=
get_entity_overwrittenby
(
method
,
i
);
entity
*
over
=
get_entity_overwrittenby
(
(
entity
*
)
method
,
i
);
is_target
|=
is_call_target
(
over
);
is_target
|=
is_call_target
(
over
);
}
}
return
(
is_target
);
return
(
is_target
);
}
}
/**
static
ir_graph
*
get_implementing_graph
(
entity
*
method
)
Given a method, find the firm graph that implements that method.
*/
static
ir_graph
*
get_implementing_graph
(
const
entity
*
method
)
{
{
ir_graph
*
graph
=
get_entity_irg
(
method
);
ir_graph
*
graph
=
get_entity_irg
(
(
entity
*
)
method
);
if
(
NULL
==
graph
)
{
if
(
NULL
==
graph
)
{
int
i
;
int
i
;
int
n_over
=
get_entity_n_overwrites
(
method
);
int
n_over
=
get_entity_n_overwrites
(
(
entity
*
)
method
);
for
(
i
=
0
;
(
NULL
==
graph
)
&&
(
i
<
n_over
);
i
++
)
{
for
(
i
=
0
;
(
NULL
==
graph
)
&&
(
i
<
n_over
);
i
++
)
{
entity
*
over
=
get_entity_overwrites
(
method
,
i
);
entity
*
over
=
get_entity_overwrites
(
(
entity
*
)
method
,
i
);
graph
=
get_implementing_graph
(
over
);
graph
=
get_implementing_graph
(
over
);
}
}
}
}
...
@@ -160,28 +180,39 @@ static ir_graph *get_implementing_graph (entity *method)
...
@@ -160,28 +180,39 @@ static ir_graph *get_implementing_graph (entity *method)
return
(
graph
);
return
(
graph
);
}
}
/**
Determine whether the give method or one of its overwriter have
been used in a call to the given graph.
*/
static
int
has_live_call
(
entity
*
method
,
ir_graph
*
graph
)
static
int
has_live_call
(
entity
*
method
,
ir_graph
*
graph
)
{
{
int
has_call
=
FALSE
;
int
has_call
=
FALSE
;
int
i
,
n_over
;
int
i
,
n_over
;
/* stop searching if a overwriting method comes with a new graph */
if
(
get_irg_ent
(
graph
)
!=
method
)
{
if
(
get_irg_ent
(
graph
)
!=
method
)
{
return
(
FALSE
);
return
(
FALSE
);
}
}
/* maybe we're called (possibly through polymorphy)? */
if
(
is_call_target
(
method
))
{
if
(
is_call_target
(
method
))
{
return
(
TRUE
);
return
(
TRUE
);
}
}
n_over
=
get_entity_n_overwrittenby
(
method
);
/* maybe we're overwritten by a method that is called? */
n_over
=
get_entity_n_overwrittenby
((
entity
*
)
method
);
for
(
i
=
0
;
!
has_call
&&
(
i
<
n_over
);
i
++
)
{
for
(
i
=
0
;
!
has_call
&&
(
i
<
n_over
);
i
++
)
{
entity
*
over
=
get_entity_overwrittenby
(
method
,
i
);
entity
*
over
=
get_entity_overwrittenby
(
(
entity
*
)
method
,
i
);
has_call
|=
has_live_call
(
over
,
graph
);
has_call
|=
has_live_call
(
over
,
graph
);
}
}
return
(
has_call
);
return
(
has_call
);
}
}
/**
Determine whether the given class is live *and* uses the given
graph at some point.
*/
static
int
has_graph
(
type
*
clazz
,
ir_graph
*
graph
)
static
int
has_graph
(
type
*
clazz
,
ir_graph
*
graph
)
{
{
int
has_the_graph
=
FALSE
;
int
has_the_graph
=
FALSE
;
...
@@ -203,6 +234,7 @@ static int has_graph (type *clazz, ir_graph *graph)
...
@@ -203,6 +234,7 @@ static int has_graph (type *clazz, ir_graph *graph)
}
/* all methods */
}
/* all methods */
}
/* _live_classes.contains (clazz) */
}
/* _live_classes.contains (clazz) */
/* our subclasses might be using that graph, no? */
n_sub
=
get_class_n_subtypes
(
clazz
);
n_sub
=
get_class_n_subtypes
(
clazz
);
for
(
i
=
0
;
!
has_the_graph
&&
(
i
<
n_sub
);
i
++
)
{
for
(
i
=
0
;
!
has_the_graph
&&
(
i
<
n_sub
);
i
++
)
{
type
*
sub
=
get_class_subtype
(
clazz
,
i
);
type
*
sub
=
get_class_subtype
(
clazz
,
i
);
...
@@ -213,6 +245,10 @@ static int has_graph (type *clazz, ir_graph *graph)
...
@@ -213,6 +245,10 @@ static int has_graph (type *clazz, ir_graph *graph)
return
(
has_the_graph
);
return
(
has_the_graph
);
}
}
/**
Determine wether the given method could be used in a call to the
given graph on a live class.
*/
static
int
has_live_class
(
entity
*
method
,
ir_graph
*
graph
)
static
int
has_live_class
(
entity
*
method
,
ir_graph
*
graph
)
{
{
int
has_class
=
FALSE
;
int
has_class
=
FALSE
;
...
@@ -220,12 +256,16 @@ static int has_live_class (entity *method, ir_graph *graph)
...
@@ -220,12 +256,16 @@ static int has_live_class (entity *method, ir_graph *graph)
int
n_over
;
int
n_over
;
type
*
clazz
;
type
*
clazz
;
/* const char *name = get_entity_name (method); */
/* stop searching when an overwriting method provides a new graph */
if
(
get_implementing_graph
(
method
)
!=
graph
)
{
if
(
get_implementing_graph
(
method
)
!=
graph
)
{
return
(
FALSE
);
return
(
FALSE
);
}
}
clazz
=
get_entity_owner
(
method
);
clazz
=
get_entity_owner
(
method
);
if
(
has_graph
(
clazz
,
graph
))
{
if
(
has_graph
(
clazz
,
graph
))
{
/* this also checks whether clazz is live*/
return
(
TRUE
);
return
(
TRUE
);
}
}
...
@@ -261,6 +301,10 @@ void rta_init ()
...
@@ -261,6 +301,10 @@ void rta_init ()
eset_insert
(
_live_graphs
,
get_irp_main_irg
());
eset_insert
(
_live_graphs
,
get_irp_main_irg
());
}
}
if
(
get_glob_type
())
{
eset_insert
(
_live_classes
,
get_glob_type
());
}
rta_fill_all
();
rta_fill_all
();
}
}
...
@@ -274,10 +318,9 @@ void rta_cleanup ()
...
@@ -274,10 +318,9 @@ void rta_cleanup ()
ir_graph
*
graph
=
get_irp_irg
(
i
);
ir_graph
*
graph
=
get_irp_irg
(
i
);
if
(
rta_check
(
graph
))
{
if
(
rta_check
(
graph
))
{
char
*
name
=
NULL
;
const
char
*
name
=
get_entity_name
(
get_irg_ent
(
graph
));
;
n_live_graphs
++
;
n_live_graphs
++
;
name
=
get_entity_name
(
get_irg_ent
(
graph
));
fprintf
(
stdout
,
"LIVE %s
\n
"
,
name
);
fprintf
(
stdout
,
"LIVE %s
\n
"
,
name
);
}
}
...
@@ -348,6 +391,9 @@ int rta_is_alive_field (entity *field)
...
@@ -348,6 +391,9 @@ int rta_is_alive_field (entity *field)
/*
/*
* $Log$
* $Log$
* Revision 1.4 2004/06/12 19:35:04 liekweg
* Kommentare eingef"ugt --flo
*
* Revision 1.3 2004/06/12 17:09:46 liekweg
* Revision 1.3 2004/06/12 17:09:46 liekweg
* RTA works, outedges breaks. "Yay." --flo
* RTA works, outedges breaks. "Yay." --flo
*
*
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment