re PR target/77826 (ICE in decompose, at wide-int.h:928 w/ -m64 -O2 and above)
2016-10-13 Richard Biener <rguenther@suse.de> PR middle-end/77826 * genmatch.c (struct capture): Add value_match member. (commutate): Preserve value_match. (lower_opt_convert): Likewise. (lower_cond): Likewise. (replace_id): Likewise. (struct dt_operand): Add value_match member. (decision_tree::cmp_node): Compare it. (decision_tree::insert_operand): Honor it when finding and when appending a DT_MATCH. (dt_operand::gen_match_op): Generate a type check after operand_equal_p if ! value_match for both GENERIC and GIMPLE. (parser::get_internal_capture_id): New helper. (parser::finish_match_operand): New function lowering @@<id>. (parser::parse_capture): Parse @@<id> as value-match. (parser::parse_expr): Use get_internal_capture_id. (parser::parse_simplify): Call finish_match_operand. (walk_captures): New helper. * match.pd (X - (X / Y) * Y -> X % Y): Use value-matching instead of operand_equal_p. ((X /[ex] A) * A -> X): Likewise. ((X | Y) ^ X -> Y & ~ X): Handle constants properly by using convert[12] and value-matching. ((A | B) & (A | C) -> A | (B & C)): Likewise. ((X | Y) | Y -> X | Y): Likewise. ((X ^ Y) ^ Y -> X): Likewise. (A - (A & B) -> ~B & A): Likewise. ((T)(P + A) - (T)P -> (T) A): Likewise. ((T)P - (T)(P + A) -> -(T) A): Likewise. ((T)(P + A) - (T)(P + B) -> (T)A - (T)B): Likewise. * doc/match-and-simplify.texi: Amend capture section. From-SVN: r241108
This commit is contained in:
parent
a946a5c38d
commit
2eef1fc113
4 changed files with 176 additions and 44 deletions
|
@ -1,3 +1,37 @@
|
|||
2016-10-13 Richard Biener <rguenther@suse.de>
|
||||
|
||||
PR middle-end/77826
|
||||
* genmatch.c (struct capture): Add value_match member.
|
||||
(commutate): Preserve value_match.
|
||||
(lower_opt_convert): Likewise.
|
||||
(lower_cond): Likewise.
|
||||
(replace_id): Likewise.
|
||||
(struct dt_operand): Add value_match member.
|
||||
(decision_tree::cmp_node): Compare it.
|
||||
(decision_tree::insert_operand): Honor it when finding and
|
||||
when appending a DT_MATCH.
|
||||
(dt_operand::gen_match_op): Generate a type check after
|
||||
operand_equal_p if ! value_match for both GENERIC and GIMPLE.
|
||||
(parser::get_internal_capture_id): New helper.
|
||||
(parser::finish_match_operand): New function lowering @@<id>.
|
||||
(parser::parse_capture): Parse @@<id> as value-match.
|
||||
(parser::parse_expr): Use get_internal_capture_id.
|
||||
(parser::parse_simplify): Call finish_match_operand.
|
||||
(walk_captures): New helper.
|
||||
* match.pd (X - (X / Y) * Y -> X % Y): Use value-matching instead
|
||||
of operand_equal_p.
|
||||
((X /[ex] A) * A -> X): Likewise.
|
||||
((X | Y) ^ X -> Y & ~ X): Handle constants properly by using
|
||||
convert[12] and value-matching.
|
||||
((A | B) & (A | C) -> A | (B & C)): Likewise.
|
||||
((X | Y) | Y -> X | Y): Likewise.
|
||||
((X ^ Y) ^ Y -> X): Likewise.
|
||||
(A - (A & B) -> ~B & A): Likewise.
|
||||
((T)(P + A) - (T)P -> (T) A): Likewise.
|
||||
((T)P - (T)(P + A) -> -(T) A): Likewise.
|
||||
((T)(P + A) - (T)(P + B) -> (T)A - (T)B): Likewise.
|
||||
* doc/match-and-simplify.texi: Amend capture section.
|
||||
|
||||
2016-10-13 Claudiu Zissulescu <claziss@synopsys.com>
|
||||
|
||||
* config/arc/arc.md (umul_600): Remove predicated variant.
|
||||
|
|
|
@ -110,7 +110,11 @@ are @code{@@} followed by a number or an identifier.
|
|||
@end smallexample
|
||||
|
||||
In this example @code{@@0} is mentioned twice which constrains the matched
|
||||
expression to have two equal operands. This example also introduces
|
||||
expression to have two equal operands. Usually matches are constraint
|
||||
to equal types. If operands may be constants and conversions are involved
|
||||
matching by value might be preferred in which case use @code{@@@@0} to
|
||||
denote a by value match and the specific operand you want to refer to
|
||||
in the result part. This example also introduces
|
||||
operands written in C code. These can be used in the expression
|
||||
replacements and are supposed to evaluate to a tree node which has to
|
||||
be a valid GIMPLE operand (so you cannot generate expressions in C code).
|
||||
|
|
150
gcc/genmatch.c
150
gcc/genmatch.c
|
@ -693,10 +693,15 @@ struct c_expr : public operand
|
|||
|
||||
struct capture : public operand
|
||||
{
|
||||
capture (source_location loc, unsigned where_, operand *what_)
|
||||
: operand (OP_CAPTURE, loc), where (where_), what (what_) {}
|
||||
capture (source_location loc, unsigned where_, operand *what_, bool value_)
|
||||
: operand (OP_CAPTURE, loc), where (where_), value_match (value_),
|
||||
what (what_) {}
|
||||
/* Identifier index for the value. */
|
||||
unsigned where;
|
||||
/* Whether in a match of two operands the compare should be for
|
||||
equal values rather than equal atoms (boils down to a type
|
||||
check or not). */
|
||||
bool value_match;
|
||||
/* The captured value. */
|
||||
operand *what;
|
||||
virtual void gen_transform (FILE *f, int, const char *, bool, int,
|
||||
|
@ -895,7 +900,8 @@ commutate (operand *op, vec<vec<user_id *> > &for_vec)
|
|||
vec<operand *> v = commutate (c->what, for_vec);
|
||||
for (unsigned i = 0; i < v.length (); ++i)
|
||||
{
|
||||
capture *nc = new capture (c->location, c->where, v[i]);
|
||||
capture *nc = new capture (c->location, c->where, v[i],
|
||||
c->value_match);
|
||||
ret.safe_push (nc);
|
||||
}
|
||||
return ret;
|
||||
|
@ -1013,7 +1019,8 @@ lower_opt_convert (operand *o, enum tree_code oper,
|
|||
{
|
||||
if (c->what)
|
||||
return new capture (c->location, c->where,
|
||||
lower_opt_convert (c->what, oper, to_oper, strip));
|
||||
lower_opt_convert (c->what, oper, to_oper, strip),
|
||||
c->value_match);
|
||||
else
|
||||
return c;
|
||||
}
|
||||
|
@ -1146,7 +1153,8 @@ lower_cond (operand *o)
|
|||
lop = lower_cond (c->what);
|
||||
|
||||
for (unsigned i = 0; i < lop.length (); ++i)
|
||||
ro.safe_push (new capture (c->location, c->where, lop[i]));
|
||||
ro.safe_push (new capture (c->location, c->where, lop[i],
|
||||
c->value_match));
|
||||
return ro;
|
||||
}
|
||||
}
|
||||
|
@ -1196,7 +1204,8 @@ lower_cond (operand *o)
|
|||
for (unsigned j = 0; j < ocmp->ops.length (); ++j)
|
||||
cmp->append_op (ocmp->ops[j]);
|
||||
cmp->is_generic = true;
|
||||
ne->ops[0] = new capture (c->location, c->where, cmp);
|
||||
ne->ops[0] = new capture (c->location, c->where, cmp,
|
||||
c->value_match);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1275,7 +1284,7 @@ replace_id (operand *o, user_id *id, id_base *with)
|
|||
if (!c->what)
|
||||
return c;
|
||||
return new capture (c->location, c->where,
|
||||
replace_id (c->what, id, with));
|
||||
replace_id (c->what, id, with), c->value_match);
|
||||
}
|
||||
else if (expr *e = dyn_cast<expr *> (o))
|
||||
{
|
||||
|
@ -1554,11 +1563,12 @@ struct dt_operand : public dt_node
|
|||
dt_operand *match_dop;
|
||||
dt_operand *parent;
|
||||
unsigned pos;
|
||||
bool value_match;
|
||||
|
||||
dt_operand (enum dt_type type, operand *op_, dt_operand *match_dop_,
|
||||
dt_operand *parent_ = 0, unsigned pos_ = 0)
|
||||
: dt_node (type), op (op_), match_dop (match_dop_),
|
||||
parent (parent_), pos (pos_) {}
|
||||
parent (parent_), pos (pos_), value_match (false) {}
|
||||
|
||||
void gen (FILE *, int, bool);
|
||||
unsigned gen_predicate (FILE *, int, const char *, bool);
|
||||
|
@ -1670,8 +1680,10 @@ decision_tree::cmp_node (dt_node *n1, dt_node *n2)
|
|||
return cmp_operand ((as_a<dt_operand *> (n1))->op,
|
||||
(as_a<dt_operand *> (n2))->op);
|
||||
else if (n1->type == dt_node::DT_MATCH)
|
||||
return ((as_a<dt_operand *> (n1))->match_dop
|
||||
== (as_a<dt_operand *> (n2))->match_dop);
|
||||
return (((as_a<dt_operand *> (n1))->match_dop
|
||||
== (as_a<dt_operand *> (n2))->match_dop)
|
||||
&& ((as_a<dt_operand *> (n1))->value_match
|
||||
== (as_a<dt_operand *> (n2))->value_match));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1841,6 +1853,7 @@ decision_tree::insert_operand (dt_node *p, operand *o, dt_operand **indexes,
|
|||
if (elm == 0)
|
||||
{
|
||||
dt_operand temp (dt_node::DT_MATCH, 0, match_op);
|
||||
temp.value_match = c->value_match;
|
||||
elm = decision_tree::find_node (p->kids, &temp);
|
||||
}
|
||||
}
|
||||
|
@ -1860,6 +1873,7 @@ at_assert_elm:
|
|||
else
|
||||
{
|
||||
p = p->append_match_op (indexes[capt_index], parent, pos);
|
||||
as_a <dt_operand *>(p)->value_match = c->value_match;
|
||||
if (c->what)
|
||||
return insert_operand (p, c->what, indexes, 0, p);
|
||||
else
|
||||
|
@ -2591,18 +2605,18 @@ dt_operand::gen_predicate (FILE *f, int indent, const char *opname, bool gimple)
|
|||
a capture-match. */
|
||||
|
||||
unsigned
|
||||
dt_operand::gen_match_op (FILE *f, int indent, const char *opname, bool gimple)
|
||||
dt_operand::gen_match_op (FILE *f, int indent, const char *opname, bool)
|
||||
{
|
||||
char match_opname[20];
|
||||
match_dop->get_name (match_opname);
|
||||
if (gimple)
|
||||
if (value_match)
|
||||
fprintf_indent (f, indent, "if (%s == %s || operand_equal_p (%s, %s, 0))\n",
|
||||
opname, match_opname, opname, match_opname);
|
||||
else
|
||||
fprintf_indent (f, indent, "if (%s == %s || (operand_equal_p (%s, %s, 0) "
|
||||
"&& types_match (%s, %s)))\n",
|
||||
opname, match_opname, opname, match_opname,
|
||||
opname, match_opname);
|
||||
else
|
||||
fprintf_indent (f, indent, "if (%s == %s || operand_equal_p (%s, %s, 0))\n",
|
||||
opname, match_opname, opname, match_opname);
|
||||
fprintf_indent (f, indent + 2, "{\n");
|
||||
return 1;
|
||||
}
|
||||
|
@ -3731,6 +3745,8 @@ private:
|
|||
const cpp_token *eat_ident (const char *);
|
||||
const char *get_number ();
|
||||
|
||||
unsigned get_internal_capture_id ();
|
||||
|
||||
id_base *parse_operation ();
|
||||
operand *parse_capture (operand *, bool);
|
||||
operand *parse_expr ();
|
||||
|
@ -3750,6 +3766,8 @@ private:
|
|||
void parse_predicates (source_location);
|
||||
void parse_operator_list (source_location);
|
||||
|
||||
void finish_match_operand (operand *);
|
||||
|
||||
cpp_reader *r;
|
||||
vec<c_expr *> active_ifs;
|
||||
vec<vec<user_id *> > active_fors;
|
||||
|
@ -3886,6 +3904,21 @@ parser::get_number ()
|
|||
return (const char *)token->val.str.text;
|
||||
}
|
||||
|
||||
/* Return a capture ID that can be used internally. */
|
||||
|
||||
unsigned
|
||||
parser::get_internal_capture_id ()
|
||||
{
|
||||
unsigned newid = capture_ids->elements ();
|
||||
/* Big enough for a 32-bit UINT_MAX plus prefix. */
|
||||
char id[13];
|
||||
bool existed;
|
||||
sprintf (id, "__%u", newid);
|
||||
capture_ids->get_or_insert (xstrdup (id), &existed);
|
||||
if (existed)
|
||||
fatal ("reserved capture id '%s' already used", id);
|
||||
return newid;
|
||||
}
|
||||
|
||||
/* Record an operator-list use for transparent for handling. */
|
||||
|
||||
|
@ -3967,6 +4000,16 @@ parser::parse_capture (operand *op, bool require_existing)
|
|||
source_location src_loc = eat_token (CPP_ATSIGN)->src_loc;
|
||||
const cpp_token *token = peek ();
|
||||
const char *id = NULL;
|
||||
bool value_match = false;
|
||||
/* For matches parse @@ as a value-match denoting the prevailing operand. */
|
||||
if (token->type == CPP_ATSIGN
|
||||
&& ! (token->flags & PREV_WHITE)
|
||||
&& parsing_match_operand)
|
||||
{
|
||||
eat_token (CPP_ATSIGN);
|
||||
token = peek ();
|
||||
value_match = true;
|
||||
}
|
||||
if (token->type == CPP_NUMBER)
|
||||
id = get_number ();
|
||||
else if (token->type == CPP_NAME)
|
||||
|
@ -3982,7 +4025,7 @@ parser::parse_capture (operand *op, bool require_existing)
|
|||
fatal_at (src_loc, "unknown capture id");
|
||||
num = next_id;
|
||||
}
|
||||
return new capture (src_loc, num, op);
|
||||
return new capture (src_loc, num, op, value_match);
|
||||
}
|
||||
|
||||
/* Parse an expression
|
||||
|
@ -4062,15 +4105,8 @@ parser::parse_expr ()
|
|||
op = parse_capture (e, false);
|
||||
else if (force_capture)
|
||||
{
|
||||
unsigned num = capture_ids->elements ();
|
||||
/* Big enough for a 32-bit UINT_MAX plus prefix. */
|
||||
char id[13];
|
||||
bool existed;
|
||||
sprintf (id, "__%u", num);
|
||||
capture_ids->get_or_insert (xstrdup (id), &existed);
|
||||
if (existed)
|
||||
fatal_at (token, "reserved capture id '%s' already used", id);
|
||||
op = new capture (token->src_loc, num, e);
|
||||
unsigned num = get_internal_capture_id ();
|
||||
op = new capture (token->src_loc, num, e, false);
|
||||
}
|
||||
else
|
||||
op = e;
|
||||
|
@ -4384,6 +4420,7 @@ parser::parse_simplify (simplify::simplify_kind kind,
|
|||
const cpp_token *loc = peek ();
|
||||
parsing_match_operand = true;
|
||||
struct operand *match = parse_op ();
|
||||
finish_match_operand (match);
|
||||
parsing_match_operand = false;
|
||||
if (match->type == operand::OP_CAPTURE && !matcher)
|
||||
fatal_at (loc, "outermost expression cannot be captured");
|
||||
|
@ -4724,6 +4761,69 @@ parser::parse_pattern ()
|
|||
eat_token (CPP_CLOSE_PAREN);
|
||||
}
|
||||
|
||||
/* Helper for finish_match_operand, collecting captures of OP in CPTS
|
||||
recursively. */
|
||||
|
||||
static void
|
||||
walk_captures (operand *op, vec<vec<capture *> > cpts)
|
||||
{
|
||||
if (! op)
|
||||
return;
|
||||
|
||||
if (capture *c = dyn_cast <capture *> (op))
|
||||
{
|
||||
cpts[c->where].safe_push (c);
|
||||
walk_captures (c->what, cpts);
|
||||
}
|
||||
else if (expr *e = dyn_cast <expr *> (op))
|
||||
for (unsigned i = 0; i < e->ops.length (); ++i)
|
||||
walk_captures (e->ops[i], cpts);
|
||||
}
|
||||
|
||||
/* Finish up OP which is a match operand. */
|
||||
|
||||
void
|
||||
parser::finish_match_operand (operand *op)
|
||||
{
|
||||
/* Look for matching captures, diagnose mis-uses of @@ and apply
|
||||
early lowering and distribution of value_match. */
|
||||
auto_vec<vec<capture *> > cpts;
|
||||
cpts.safe_grow_cleared (capture_ids->elements ());
|
||||
walk_captures (op, cpts);
|
||||
for (unsigned i = 0; i < cpts.length (); ++i)
|
||||
{
|
||||
capture *value_match = NULL;
|
||||
for (unsigned j = 0; j < cpts[i].length (); ++j)
|
||||
{
|
||||
if (cpts[i][j]->value_match)
|
||||
{
|
||||
if (value_match)
|
||||
fatal_at (cpts[i][j]->location, "duplicate @@");
|
||||
value_match = cpts[i][j];
|
||||
}
|
||||
}
|
||||
if (cpts[i].length () == 1 && value_match)
|
||||
fatal_at (value_match->location, "@@ without a matching capture");
|
||||
if (value_match)
|
||||
{
|
||||
/* Duplicate prevailing capture with the existing ID, create
|
||||
a fake ID and rewrite all captures to use it. This turns
|
||||
@@1 into @__<newid>@1 and @1 into @__<newid>. */
|
||||
value_match->what = new capture (value_match->location,
|
||||
value_match->where,
|
||||
value_match->what, false);
|
||||
/* Create a fake ID and rewrite all captures to use it. */
|
||||
unsigned newid = get_internal_capture_id ();
|
||||
for (unsigned j = 0; j < cpts[i].length (); ++j)
|
||||
{
|
||||
cpts[i][j]->where = newid;
|
||||
cpts[i][j]->value_match = true;
|
||||
}
|
||||
}
|
||||
cpts[i].release ();
|
||||
}
|
||||
}
|
||||
|
||||
/* Main entry of the parser. Repeatedly parse outer control structures. */
|
||||
|
||||
parser::parser (cpp_reader *r_)
|
||||
|
|
30
gcc/match.pd
30
gcc/match.pd
|
@ -334,11 +334,8 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
|
|||
|
||||
/* X - (X / Y) * Y is the same as X % Y. */
|
||||
(simplify
|
||||
(minus (convert1? @2) (convert2? (mult:c (trunc_div @0 @1) @1)))
|
||||
/* We cannot use matching captures here, since in the case of
|
||||
constants we really want the type of @0, not @2. */
|
||||
(if (operand_equal_p (@0, @2, 0)
|
||||
&& (INTEGRAL_TYPE_P (type) || VECTOR_INTEGER_TYPE_P (type)))
|
||||
(minus (convert1? @0) (convert2? (mult:c (trunc_div @@0 @@1) @1)))
|
||||
(if (INTEGRAL_TYPE_P (type) || VECTOR_INTEGER_TYPE_P (type))
|
||||
(convert (trunc_mod @0 @1))))
|
||||
|
||||
/* Optimize TRUNC_MOD_EXPR by a power of two into a BIT_AND_EXPR,
|
||||
|
@ -714,7 +711,7 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
|
|||
|
||||
/* (X | Y) ^ X -> Y & ~ X*/
|
||||
(simplify
|
||||
(bit_xor:c (convert? (bit_ior:c @0 @1)) (convert? @0))
|
||||
(bit_xor:c (convert1? (bit_ior:c @@0 @1)) (convert2? @0))
|
||||
(if (tree_nop_conversion_p (type, TREE_TYPE (@0)))
|
||||
(convert (bit_and @1 (bit_not @0)))))
|
||||
|
||||
|
@ -747,7 +744,7 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
|
|||
(for op (bit_and bit_ior bit_xor)
|
||||
rop (bit_ior bit_and bit_and)
|
||||
(simplify
|
||||
(op (convert? (rop:c @0 @1)) (convert? (rop:c @0 @2)))
|
||||
(op (convert? (rop:c @@0 @1)) (convert? (rop:c @0 @2)))
|
||||
(if (tree_nop_conversion_p (type, TREE_TYPE (@1))
|
||||
&& tree_nop_conversion_p (type, TREE_TYPE (@2)))
|
||||
(rop (convert @0) (op (convert @1) (convert @2))))))
|
||||
|
@ -757,11 +754,11 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
|
|||
(X | Y) | Y -> X | Y */
|
||||
(for op (bit_and bit_ior)
|
||||
(simplify
|
||||
(op:c (convert?@2 (op:c @0 @1)) (convert? @1))
|
||||
(op:c (convert1?@2 (op:c @0 @@1)) (convert2? @1))
|
||||
@2))
|
||||
/* (X ^ Y) ^ Y -> X */
|
||||
(simplify
|
||||
(bit_xor:c (convert? (bit_xor:c @0 @1)) (convert? @1))
|
||||
(bit_xor:c (convert1? (bit_xor:c @0 @@1)) (convert2? @1))
|
||||
(convert @0))
|
||||
/* (X & Y) & (X & Z) -> (X & Y) & Z
|
||||
(X | Y) | (X | Z) -> (X | Y) | Z */
|
||||
|
@ -991,7 +988,7 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
|
|||
|
||||
/* Fold A - (A & B) into ~B & A. */
|
||||
(simplify
|
||||
(minus (convert? @0) (convert?:s (bit_and:cs @0 @1)))
|
||||
(minus (convert1? @0) (convert2?:s (bit_and:cs @@0 @1)))
|
||||
(if (tree_nop_conversion_p (type, TREE_TYPE (@0))
|
||||
&& tree_nop_conversion_p (type, TREE_TYPE (@1)))
|
||||
(convert (bit_and (bit_not @1) @0))))
|
||||
|
@ -1206,7 +1203,7 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
|
|||
/* (T)(P + A) - (T)P -> (T) A */
|
||||
(for add (plus pointer_plus)
|
||||
(simplify
|
||||
(minus (convert (add @0 @1))
|
||||
(minus (convert (add @@0 @1))
|
||||
(convert @0))
|
||||
(if (element_precision (type) <= element_precision (TREE_TYPE (@1))
|
||||
/* For integer types, if A has a smaller type
|
||||
|
@ -1231,7 +1228,7 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
|
|||
(for add (plus pointer_plus)
|
||||
(simplify
|
||||
(minus (convert @0)
|
||||
(convert (add @0 @1)))
|
||||
(convert (add @@0 @1)))
|
||||
(if (element_precision (type) <= element_precision (TREE_TYPE (@1))
|
||||
/* For integer types, if A has a smaller type
|
||||
than T the result depends on the possible
|
||||
|
@ -1254,7 +1251,7 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
|
|||
/* (T)(P + A) - (T)(P + B) -> (T)A - (T)B */
|
||||
(for add (plus pointer_plus)
|
||||
(simplify
|
||||
(minus (convert (add @0 @1))
|
||||
(minus (convert (add @@0 @1))
|
||||
(convert (add @0 @2)))
|
||||
(if (element_precision (type) <= element_precision (TREE_TYPE (@1))
|
||||
/* For integer types, if A has a smaller type
|
||||
|
@ -1781,11 +1778,8 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
|
|||
|
||||
/* (X /[ex] A) * A -> X. */
|
||||
(simplify
|
||||
(mult (convert1? (exact_div @0 @1)) (convert2? @2))
|
||||
/* We cannot use matching captures here, since in the case of
|
||||
constants we don't see the second conversion. */
|
||||
(if (operand_equal_p (@1, @2, 0))
|
||||
(convert @0)))
|
||||
(mult (convert1? (exact_div @0 @@1)) (convert2? @1))
|
||||
(convert @0))
|
||||
|
||||
/* Canonicalization of binary operations. */
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue