optimizer: extend the reductions like (equal? x y) => (eq? x y)
This kind of reductions were applied only when x or y was a constant. Classify the relevant predicates in 4 categories. In particular, if <expr> satisfy pred? we can use this classification to apply the correct reduction: (equal? <expr> y) ==> [no reduction, unless y has a different type] (equal? <expr> y) ==> (eqv? <expr> y) (equal? <expr> y) ==> (eq? <expr> y) (equal? <expr> y) ==> (begin <expr> (pred? y))
This commit is contained in:
parent
7c1cb1a2f0
commit
5833390396
|
@ -1076,18 +1076,45 @@
|
|||
'(lambda (x) (if (not (not (not x))) 1 2)))
|
||||
(test-comp '(lambda (x) (not x))
|
||||
'(lambda (x) (if x #f #t)))
|
||||
(test-comp '(lambda (x) (not x))
|
||||
'(lambda (x) (eq? x #f)))
|
||||
(test-comp '(lambda (x) (not x))
|
||||
'(lambda (x) (eq? #f x)))
|
||||
|
||||
(test-comp '(lambda (x) (null? x))
|
||||
'(lambda (x) (eq? x '())))
|
||||
(test-comp '(lambda (x) (null? x))
|
||||
'(lambda (x) (eq? '() x)))
|
||||
|
||||
(test-comp '(lambda (x) (eq? x 7))
|
||||
'(lambda (x) (equal? x 7)))
|
||||
(let ([test-equal-reduction
|
||||
(lambda (val)
|
||||
(test-comp `(lambda (x) (equal? x ,val))
|
||||
`(lambda (x) (eq? x ,val)))
|
||||
(test-comp `(lambda (x) (equal? ,val x))
|
||||
`(lambda (x) (eq? ,val x)))
|
||||
(test-comp `(lambda (x) (eqv? x ,val))
|
||||
`(lambda (x) (eq? x ,val)))
|
||||
(test-comp `(lambda (x) (eqv? ,val x))
|
||||
`(lambda (x) (eq? ,val x))))]
|
||||
[test-equal-reduction/only-eqv
|
||||
(lambda (val)
|
||||
(test-comp `(lambda (x) (equal? x ,val))
|
||||
`(lambda (x) (eqv? x ,val)))
|
||||
(test-comp `(lambda (x) (equal? ,val x))
|
||||
`(lambda (x) (eqv? ,val x)))
|
||||
(test-comp `(lambda (x) (equal? x ,val))
|
||||
`(lambda (x) (eq? x ,val))
|
||||
#f)
|
||||
(test-comp `(lambda (x) (equal? ,val x))
|
||||
`(lambda (x) (eq? ,val x))
|
||||
#f)
|
||||
(test-comp `(lambda (x) (eqv? x ,val))
|
||||
`(lambda (x) (eq? x ,val))
|
||||
#f)
|
||||
(test-comp `(lambda (x) (eqv? ,val x))
|
||||
`(lambda (x) (eq? ,val x))
|
||||
#f))])
|
||||
(test-equal-reduction 7)
|
||||
(test-equal-reduction/only-eqv 7.0)
|
||||
(test-equal-reduction/only-eqv '(expt 2 100))
|
||||
(test-equal-reduction #\a)
|
||||
(test-equal-reduction/only-eqv #\u100)
|
||||
(test-equal-reduction ''a)
|
||||
(test-equal-reduction ''#:a)
|
||||
(test-equal-reduction '(exact-positive-integer? (random 2))))
|
||||
|
||||
|
||||
(test-comp '(lambda (x) (eq? 7 x))
|
||||
'(lambda (x) (equal? 7 x)))
|
||||
(test-comp '(lambda (x) (eq? x 7))
|
||||
|
@ -2129,12 +2156,19 @@
|
|||
(let ([test-pred-implies-val
|
||||
(lambda (pred? val)
|
||||
(test-comp `(lambda (x) (if (,pred? x) ,val 0))
|
||||
`(lambda (x) (if (,pred? x) x 0))))])
|
||||
`(lambda (x) (if (,pred? x) x 0)))
|
||||
(test-comp `(lambda (x) (eq? x ,val))
|
||||
`(lambda (x) (,pred? x)))
|
||||
(test-comp `(lambda (x) (eq? ,val x))
|
||||
`(lambda (x) (,pred? x))))])
|
||||
(test-pred-implies-val 'null? 'null)
|
||||
(test-pred-implies-val 'void? '(void))
|
||||
(test-pred-implies-val 'eof-object? 'eof)
|
||||
(test-pred-implies-val 'k:true-object? '#t)
|
||||
(test-pred-implies-val 'not '#f))
|
||||
(test-comp '(lambda (f) (eq? (f) (begin (newline) null)))
|
||||
'(lambda (x) (begin (newline) null) (null? (f)))
|
||||
#f)
|
||||
(test-comp '(lambda (x) (if (null? x) 1 0) null)
|
||||
'(lambda (x) (if (null? x) 1 0) x)
|
||||
#f)
|
||||
|
|
|
@ -48,6 +48,13 @@
|
|||
#define INITIAL_INLINING_FUEL 32
|
||||
#define INITIAL_FLATTENING_FUEL 16
|
||||
|
||||
/* Clasification for predicates.
|
||||
Each one implies the smaller. */
|
||||
#define RLV_IS_RELEVANT 1 /* The predicate is remembered by the optimizer */
|
||||
#define RLV_EQV_TESTEABLE 2 /* (equal? x <pred>) can be replaced by (eqv? x <pred>) */
|
||||
#define RLV_EQ_TESTEABLE 3 /* (equal? x <pred>) can be replaced by (eq? x <pred>) */
|
||||
#define RLV_SINGLETON 4 /* Recognizes a single value */
|
||||
|
||||
struct Optimize_Info
|
||||
{
|
||||
MZTAG_IF_REQUIRED
|
||||
|
@ -4353,27 +4360,6 @@ static Scheme_Object *finish_optimize_application2(Scheme_App2_Rec *app, Optimiz
|
|||
return finish_optimize_any_application((Scheme_Object *)app, rator, 1, info, context);
|
||||
}
|
||||
|
||||
int scheme_eq_testable_constant(Scheme_Object *v)
|
||||
{
|
||||
if (SCHEME_SYMBOLP(v)
|
||||
|| SCHEME_KEYWORDP(v)
|
||||
|| SCHEME_FALSEP(v)
|
||||
|| SAME_OBJ(v, scheme_true)
|
||||
|| SCHEME_NULLP(v)
|
||||
|| SCHEME_VOIDP(v)
|
||||
|| SCHEME_EOFP(v))
|
||||
return 1;
|
||||
|
||||
if (SCHEME_CHARP(v) && (SCHEME_CHAR_VAL(v) < 256))
|
||||
return 1;
|
||||
|
||||
if (SCHEME_INTP(v)
|
||||
&& IN_FIXNUM_RANGE_ON_ALL_PLATFORMS(SCHEME_INT_VAL(v)))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Scheme_Object *optimize_application3(Scheme_Object *o, Optimize_Info *info, int context)
|
||||
{
|
||||
Scheme_App3_Rec *app;
|
||||
|
@ -4579,40 +4565,61 @@ static Scheme_Object *finish_optimize_application3(Scheme_App3_Rec *app, Optimiz
|
|||
return make_discarding_sequence_3(app->rand1, app->rand2, scheme_true, info);
|
||||
}
|
||||
{
|
||||
Scheme_Object *pred1, *pred2;
|
||||
Scheme_Object *pred1, *pred2, *pred_new = NULL;
|
||||
int rel1=0, rel2=0, rel_max, eq_type=0;
|
||||
|
||||
pred1 = expr_implies_predicate(app->rand1, info);
|
||||
if (pred1) {
|
||||
pred2 = expr_implies_predicate(app->rand2, info);
|
||||
if (pred2) {
|
||||
if (predicate_implies_not(pred1, pred2)
|
||||
|| predicate_implies_not(pred2, pred1)) {
|
||||
info->preserves_marks = 1;
|
||||
info->single_result = 1;
|
||||
return make_discarding_sequence_3(app->rand1, app->rand2, scheme_false, info);
|
||||
}
|
||||
pred2 = expr_implies_predicate(app->rand2, info);
|
||||
rel1 = relevant_predicate(pred1);
|
||||
rel2 = relevant_predicate(pred2);
|
||||
if ((pred1 && pred2)
|
||||
&& (predicate_implies_not(pred1, pred2)
|
||||
|| predicate_implies_not(pred2, pred1))) {
|
||||
info->preserves_marks = 1;
|
||||
info->single_result = 1;
|
||||
return make_discarding_sequence_3(app->rand1, app->rand2, scheme_false, info);
|
||||
}
|
||||
|
||||
/* Try to transform it into a predicate */
|
||||
if (rel1 >= RLV_SINGLETON) {
|
||||
Scheme_Object *new_app;
|
||||
new_app = make_optimize_prim_application2(pred1, app->rand2, info, context);
|
||||
return make_discarding_sequence(app->rand1, new_app, info);
|
||||
}
|
||||
if (rel2 >= RLV_SINGLETON) {
|
||||
Scheme_Object *new_app;
|
||||
new_app = make_optimize_prim_application2(pred2, app->rand1, info, context);
|
||||
return make_discarding_reverse_sequence(app->rand2, new_app, info);
|
||||
}
|
||||
|
||||
/* Optimize `equal?' or `eqv?' test on certain types
|
||||
to `eqv?` or `eq?'. This is especially helpful for the JIT. */
|
||||
if (SAME_OBJ(app->rator, scheme_eqv_proc))
|
||||
eq_type = RLV_EQV_TESTEABLE;
|
||||
if (SAME_OBJ(app->rator, scheme_eq_proc))
|
||||
eq_type = RLV_EQ_TESTEABLE;
|
||||
|
||||
rel_max = (rel1 >= rel2) ? rel1 : rel2;
|
||||
if (rel_max >= RLV_EQ_TESTEABLE && eq_type < RLV_EQ_TESTEABLE)
|
||||
pred_new = scheme_eq_proc;
|
||||
else if (rel_max >= RLV_EQV_TESTEABLE && eq_type < RLV_EQV_TESTEABLE)
|
||||
pred_new = scheme_eqv_proc;
|
||||
|
||||
if (pred_new) {
|
||||
app->rator = pred_new;
|
||||
SCHEME_APPN_FLAGS(app) |= (APPN_FLAG_IMMED | APPN_FLAG_SFS_TAIL);
|
||||
scheme_check_leaf_rator(pred_new, &rator_flags);
|
||||
|
||||
/* eq? and eqv? are foldable */
|
||||
if (all_vals) {
|
||||
le = try_optimize_fold(app->rator, NULL, (Scheme_Object *)app, info);
|
||||
if (le)
|
||||
return le;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Optimize `equal?' or `eqv?' test on certain types
|
||||
to `eq?'. This is especially helpful for the JIT. */
|
||||
if ((SAME_OBJ(app->rator, scheme_equal_proc)
|
||||
|| SAME_OBJ(app->rator, scheme_eqv_proc))
|
||||
&& (scheme_eq_testable_constant(app->rand1)
|
||||
|| scheme_eq_testable_constant(app->rand2))) {
|
||||
app->rator = scheme_eq_proc;
|
||||
SCHEME_APPN_FLAGS(app) |= (APPN_FLAG_IMMED | APPN_FLAG_SFS_TAIL);
|
||||
scheme_check_leaf_rator(scheme_eq_proc, &rator_flags);
|
||||
|
||||
/* eq? is foldable */
|
||||
if (all_vals) {
|
||||
le = try_optimize_fold(app->rator, NULL, (Scheme_Object *)app, info);
|
||||
if (le)
|
||||
return le;
|
||||
}
|
||||
}
|
||||
|
||||
info->preserves_marks = !!(rator_flags & LAMBDA_PRESERVES_MARKS);
|
||||
info->single_result = !!(rator_flags & LAMBDA_SINGLE_RESULT);
|
||||
if (rator_flags & LAMBDA_RESULT_TENTATIVE) {
|
||||
|
@ -4714,22 +4721,6 @@ static Scheme_Object *finish_optimize_application3(Scheme_App3_Rec *app, Optimiz
|
|||
app->rator = scheme_unsafe_fxrshift_proc;
|
||||
app->rand2 = scheme_make_integer(-(SCHEME_INT_VAL(app->rand2)));
|
||||
}
|
||||
} else if (SAME_OBJ(app->rator, scheme_eq_proc)) {
|
||||
/* Try optimize: (eq? x #f) => (not x) and (eq? x '()) => (null? x) */
|
||||
if (SCHEME_FALSEP(app->rand1)) {
|
||||
info->size -= 1;
|
||||
return make_optimize_prim_application2(scheme_not_proc, app->rand2, info, context);
|
||||
} else if (SCHEME_FALSEP(app->rand2)) {
|
||||
info->size -= 1;
|
||||
return make_optimize_prim_application2(scheme_not_proc, app->rand1, info, context);
|
||||
}
|
||||
if (SCHEME_NULLP(app->rand1)) {
|
||||
info->size -= 1;
|
||||
return make_optimize_prim_application2(scheme_null_p_proc, app->rand2, info, context);
|
||||
} else if (SCHEME_NULLP(app->rand2)) {
|
||||
info->size -= 1;
|
||||
return make_optimize_prim_application2(scheme_null_p_proc, app->rand1, info, context);
|
||||
}
|
||||
} else if (IS_NAMED_PRIM(app->rator, "string=?")) {
|
||||
if (SAME_TYPE(SCHEME_TYPE(app->rand1), scheme_char_string_type)
|
||||
&& SAME_TYPE(SCHEME_TYPE(app->rand2), scheme_char_string_type)) {
|
||||
|
@ -5286,32 +5277,37 @@ static int relevant_predicate(Scheme_Object *pred)
|
|||
The predicate_implies() and predicate_implies_not() functions must
|
||||
be kept in sync with this list. */
|
||||
|
||||
return (SAME_OBJ(pred, scheme_pair_p_proc)
|
||||
|| SAME_OBJ(pred, scheme_null_p_proc)
|
||||
|| SAME_OBJ(pred, scheme_mpair_p_proc)
|
||||
|| SAME_OBJ(pred, scheme_box_p_proc)
|
||||
|| SAME_OBJ(pred, scheme_list_p_proc)
|
||||
|| SAME_OBJ(pred, scheme_list_pair_p_proc)
|
||||
|| SAME_OBJ(pred, scheme_string_p_proc)
|
||||
|| SAME_OBJ(pred, scheme_byte_string_p_proc)
|
||||
|| SAME_OBJ(pred, scheme_vector_p_proc)
|
||||
|| SAME_OBJ(pred, scheme_symbol_p_proc)
|
||||
|| SAME_OBJ(pred, scheme_keyword_p_proc)
|
||||
|| SAME_OBJ(pred, scheme_procedure_p_proc)
|
||||
|| SAME_OBJ(pred, scheme_syntax_p_proc)
|
||||
|| SAME_OBJ(pred, scheme_fixnum_p_proc)
|
||||
|| SAME_OBJ(pred, scheme_flonum_p_proc)
|
||||
|| SAME_OBJ(pred, scheme_extflonum_p_proc)
|
||||
|| SAME_OBJ(pred, scheme_number_p_proc)
|
||||
|| SAME_OBJ(pred, scheme_real_p_proc)
|
||||
|| SAME_OBJ(pred, scheme_char_p_proc)
|
||||
|| SAME_OBJ(pred, scheme_interned_char_p_proc)
|
||||
|| SAME_OBJ(pred, scheme_void_p_proc)
|
||||
|| SAME_OBJ(pred, scheme_eof_object_p_proc)
|
||||
|| SAME_OBJ(pred, scheme_boolean_p_proc)
|
||||
|| SAME_OBJ(pred, scheme_true_object_p_proc)
|
||||
|| SAME_OBJ(pred, scheme_not_proc)
|
||||
);
|
||||
if (SAME_OBJ(pred, scheme_pair_p_proc)
|
||||
|| SAME_OBJ(pred, scheme_list_p_proc)
|
||||
|| SAME_OBJ(pred, scheme_list_pair_p_proc)
|
||||
|| SAME_OBJ(pred, scheme_mpair_p_proc)
|
||||
|| SAME_OBJ(pred, scheme_box_p_proc)
|
||||
|| SAME_OBJ(pred, scheme_string_p_proc)
|
||||
|| SAME_OBJ(pred, scheme_byte_string_p_proc)
|
||||
|| SAME_OBJ(pred, scheme_vector_p_proc)
|
||||
|| SAME_OBJ(pred, scheme_procedure_p_proc)
|
||||
|| SAME_OBJ(pred, scheme_syntax_p_proc)
|
||||
|| SAME_OBJ(pred, scheme_extflonum_p_proc))
|
||||
return RLV_IS_RELEVANT;
|
||||
if (SAME_OBJ(pred, scheme_char_p_proc)
|
||||
|| SAME_OBJ(pred, scheme_flonum_p_proc)
|
||||
|| SAME_OBJ(pred, scheme_number_p_proc)
|
||||
|| SAME_OBJ(pred, scheme_real_p_proc))
|
||||
return RLV_EQV_TESTEABLE;
|
||||
if (SAME_OBJ(pred, scheme_symbol_p_proc)
|
||||
|| SAME_OBJ(pred, scheme_keyword_p_proc)
|
||||
|| SAME_OBJ(pred, scheme_fixnum_p_proc)
|
||||
|| SAME_OBJ(pred, scheme_interned_char_p_proc)
|
||||
|| SAME_OBJ(pred, scheme_boolean_p_proc))
|
||||
return RLV_EQ_TESTEABLE;
|
||||
if (SAME_OBJ(pred, scheme_null_p_proc)
|
||||
|| SAME_OBJ(pred, scheme_void_p_proc)
|
||||
|| SAME_OBJ(pred, scheme_eof_object_p_proc)
|
||||
|| SAME_OBJ(pred, scheme_true_object_p_proc)
|
||||
|| SAME_OBJ(pred, scheme_not_proc))
|
||||
return RLV_SINGLETON;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int predicate_implies(Scheme_Object *pred1, Scheme_Object *pred2)
|
||||
|
|
|
@ -340,6 +340,27 @@ static Scheme_Object *resolve_application2(Scheme_Object *o, Resolve_Info *orig_
|
|||
return (Scheme_Object *)app;
|
||||
}
|
||||
|
||||
int eq_testable_constant(Scheme_Object *v)
|
||||
{
|
||||
if (SCHEME_SYMBOLP(v)
|
||||
|| SCHEME_KEYWORDP(v)
|
||||
|| SCHEME_FALSEP(v)
|
||||
|| SAME_OBJ(v, scheme_true)
|
||||
|| SCHEME_NULLP(v)
|
||||
|| SCHEME_VOIDP(v)
|
||||
|| SCHEME_EOFP(v))
|
||||
return 1;
|
||||
|
||||
if (SCHEME_CHARP(v) && (SCHEME_CHAR_VAL(v) < 256))
|
||||
return 1;
|
||||
|
||||
if (SCHEME_INTP(v)
|
||||
&& IN_FIXNUM_RANGE_ON_ALL_PLATFORMS(SCHEME_INT_VAL(v)))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void set_app3_eval_type(Scheme_App3_Rec *app)
|
||||
/* set flags used for a shortcut in the interpreter */
|
||||
{
|
||||
|
@ -425,8 +446,8 @@ static Scheme_Object *resolve_application3(Scheme_Object *o, Resolve_Info *orig_
|
|||
optimization layer, and we keep it just in case.*/
|
||||
if ((SAME_OBJ(app->rator, scheme_equal_proc)
|
||||
|| SAME_OBJ(app->rator, scheme_eqv_proc))
|
||||
&& (scheme_eq_testable_constant(app->rand1)
|
||||
|| scheme_eq_testable_constant(app->rand2))) {
|
||||
&& (eq_testable_constant(app->rand1)
|
||||
|| eq_testable_constant(app->rand2))) {
|
||||
app->rator = scheme_eq_proc;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user