optimizer: use type predicates to calculate local types
The functions expr_implies_predicate was very similar to expr_produces_local_type, and slighty more general. Merging them, is possible to use the type information is expressions where the optimizer used only the local types that were visible at the definition. For example, this is useful in this expression to transform bitwise-xor to it's unsafe version. (lambda (x) (when (fixnum? x) (bitwise-xor x #xff)))
This commit is contained in:
parent
ced25315ac
commit
bbbe99db43
|
@ -22,8 +22,8 @@
|
|||
(namespace-require 'racket/flonum)
|
||||
(namespace-require 'racket/extflonum)
|
||||
(namespace-require 'racket/fixnum)
|
||||
(namespace-require 'racket/unsafe/ops)
|
||||
(namespace-require 'racket/unsafe/undefined)
|
||||
#;(namespace-require '(rename '#%kernel k:map map))
|
||||
(eval '(define-values (prop:thing thing? thing-ref)
|
||||
(make-struct-type-property 'thing)))
|
||||
(eval '(struct rock (x) #:property prop:thing 'yes))
|
||||
|
@ -2806,7 +2806,49 @@
|
|||
(test-use-unsafe 'pair? 'cdr 'unsafe-cdr)
|
||||
(test-use-unsafe 'mpair? 'mcar 'unsafe-mcar)
|
||||
(test-use-unsafe 'mpair? 'mcdr 'unsafe-mcdr)
|
||||
(test-use-unsafe 'box? 'unbox 'unsafe-unbox))
|
||||
(test-use-unsafe 'box? 'unbox 'unsafe-unbox)
|
||||
(test-use-unsafe 'vector? 'vector-length 'unsafe-vector-length))
|
||||
|
||||
(let ([test-use-unsafe-fxbinary
|
||||
(lambda (op unsafe-op)
|
||||
(test-comp `(lambda (vx vy)
|
||||
(let ([x (vector-length vx)]
|
||||
[y (vector-length vy)])
|
||||
(,op x y)))
|
||||
`(lambda (vx vy)
|
||||
(let ([x (vector-length vx)]
|
||||
[y (vector-length vy)])
|
||||
(,unsafe-op x y))))
|
||||
(test-comp `(lambda (x y)
|
||||
(when (and (fixnum? x) (fixnum? y))
|
||||
(,op x y)))
|
||||
`(lambda (x y)
|
||||
(when (and (fixnum? x) (fixnum? y))
|
||||
(,unsafe-op x y))))
|
||||
(test-comp `(lambda (x y)
|
||||
(when (and (fixnum? x) (fixnum? y) (zero? (random 2)))
|
||||
(,op x y)))
|
||||
`(lambda (x y)
|
||||
(when (and (fixnum? x) (fixnum? y) (zero? (random 2)))
|
||||
(,unsafe-op x y)))))])
|
||||
(test-use-unsafe-fxbinary 'bitwise-and 'unsafe-fxand)
|
||||
(test-use-unsafe-fxbinary 'bitwise-ior 'unsafe-fxior)
|
||||
(test-use-unsafe-fxbinary 'bitwise-xor 'unsafe-fxxor))
|
||||
|
||||
;test special case for bitwise-and and fixnum?
|
||||
(test-comp '(lambda (x)
|
||||
(let ([y (bitwise-and x 2)])
|
||||
(list y y (fixnum? y))))
|
||||
'(lambda (x)
|
||||
(let ([y (bitwise-and x 2)])
|
||||
(list y y #t))))
|
||||
(test-comp '(lambda (x)
|
||||
(let ([y (bitwise-and x 2)])
|
||||
(fixnum? x)))
|
||||
'(lambda (x)
|
||||
(let ([y (bitwise-and x 2)])
|
||||
#t))
|
||||
#f)
|
||||
|
||||
(test-comp `(module m racket/base
|
||||
(require racket/unsafe/ops)
|
||||
|
@ -2837,7 +2879,7 @@
|
|||
(- (expt 2 31) 2))
|
||||
#f)
|
||||
|
||||
;; Propagate type impliciations from RHS:
|
||||
;; Propagate type implications from RHS:
|
||||
(test-comp '(lambda (x)
|
||||
(let ([y (car x)])
|
||||
(list (cdr x) y (car x) y)))
|
||||
|
|
|
@ -132,6 +132,8 @@ static void add_type(Optimize_Info *info, int pos, Scheme_Object *pred);
|
|||
static void merge_types(Optimize_Info *src_info, Optimize_Info *info, int delta);
|
||||
static Scheme_Object *lookup_constant_proc(Optimize_Info *info, Scheme_Object *rand, int delta);
|
||||
|
||||
static int predicate_to_local_type(Scheme_Object *pred);
|
||||
static Scheme_Object *expr_implies_predicate(Scheme_Object *expr, Optimize_Info *info, int delta, int fuel);
|
||||
static void optimize_mutated(Optimize_Info *info, int pos);
|
||||
static void optimize_produces_local_type(Optimize_Info *info, int pos, int ct);
|
||||
static int produces_local_type(Scheme_Object *rator, int argc);
|
||||
|
@ -2041,17 +2043,7 @@ Scheme_Object *optimize_for_inline(Optimize_Info *info, Scheme_Object *le, int a
|
|||
|
||||
static int is_local_type_expression(Scheme_Object *expr, Optimize_Info *info)
|
||||
{
|
||||
int ty;
|
||||
|
||||
ty = scheme_expr_produces_local_type(expr);
|
||||
if (ty) return ty;
|
||||
|
||||
if (SAME_TYPE(SCHEME_TYPE(expr), scheme_local_type)) {
|
||||
ty = optimize_is_local_type_valued(info, SCHEME_LOCAL_POS(expr));
|
||||
if (ty) return ty;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return predicate_to_local_type(expr_implies_predicate(expr, info, 0, 5));
|
||||
}
|
||||
|
||||
static void register_local_argument_types(Scheme_App_Rec *app, Scheme_App2_Rec *app2, Scheme_App3_Rec *app3,
|
||||
|
@ -2368,96 +2360,6 @@ static int produces_local_type(Scheme_Object *rator, int argc)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int expr_produces_local_type(Scheme_Object *expr, int fuel)
|
||||
/* can be called by the JIT; beware that the validator must be
|
||||
able to reconstruct the result in a shallow way, so don't
|
||||
make the result of a function call depend on its arguments */
|
||||
{
|
||||
if (fuel <= 0) return 0;
|
||||
|
||||
while (1) {
|
||||
switch (SCHEME_TYPE(expr)) {
|
||||
case scheme_application_type:
|
||||
{
|
||||
Scheme_App_Rec *app = (Scheme_App_Rec *)expr;
|
||||
return produces_local_type(app->args[0], app->num_args);
|
||||
}
|
||||
break;
|
||||
case scheme_application2_type:
|
||||
{
|
||||
Scheme_App2_Rec *app = (Scheme_App2_Rec *)expr;
|
||||
return produces_local_type(app->rator, 1);
|
||||
}
|
||||
break;
|
||||
case scheme_application3_type:
|
||||
{
|
||||
Scheme_App3_Rec *app = (Scheme_App3_Rec *)expr;
|
||||
|
||||
if (SCHEME_PRIMP(app->rator)
|
||||
&& (SCHEME_PRIM_PROC_OPT_FLAGS(app->rator) & SCHEME_PRIM_IS_BINARY_INLINED)
|
||||
&& IS_NAMED_PRIM(app->rator, "bitwise-and")) {
|
||||
/* Assume that a fixnum argument to bitwise-and will never get lost,
|
||||
and so the validator will be able to confirm that a `bitwise-and`
|
||||
combination produces a fixnum. */
|
||||
if ((SCHEME_INTP(app->rand1)
|
||||
&& IN_FIXNUM_RANGE_ON_ALL_PLATFORMS(SCHEME_INT_VAL(app->rand1)))
|
||||
|| (SCHEME_INTP(app->rand2)
|
||||
&& IN_FIXNUM_RANGE_ON_ALL_PLATFORMS(SCHEME_INT_VAL(app->rand2))))
|
||||
return SCHEME_LOCAL_TYPE_FIXNUM;
|
||||
}
|
||||
|
||||
return produces_local_type(app->rator, 2);
|
||||
}
|
||||
break;
|
||||
case scheme_branch_type:
|
||||
{
|
||||
Scheme_Branch_Rec *b = (Scheme_Branch_Rec *)expr;
|
||||
int t1, t2;
|
||||
|
||||
t1 = expr_produces_local_type(b->tbranch, fuel / 2);
|
||||
if (t1) {
|
||||
t2 = expr_produces_local_type(b->fbranch, fuel / 2);
|
||||
return ((t1 == t2) ? t1 : 0);
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case scheme_sequence_type:
|
||||
{
|
||||
Scheme_Sequence *seq = (Scheme_Sequence *)expr;
|
||||
|
||||
expr = seq->array[seq->count-1];
|
||||
break;
|
||||
}
|
||||
case scheme_compiled_let_void_type:
|
||||
{
|
||||
Scheme_Let_Header *lh = (Scheme_Let_Header *)expr;
|
||||
int i;
|
||||
expr = lh->body;
|
||||
for (i = 0; i < lh->num_clauses; i++) {
|
||||
expr = ((Scheme_Compiled_Let_Value *)expr)->body;
|
||||
}
|
||||
/* check expr again */
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (SCHEME_FLOATP(expr))
|
||||
return SCHEME_LOCAL_TYPE_FLONUM;
|
||||
if (SCHEME_LONG_DBLP(expr))
|
||||
return SCHEME_LOCAL_TYPE_EXTFLONUM;
|
||||
if (SCHEME_INTP(expr)
|
||||
&& IN_FIXNUM_RANGE_ON_ALL_PLATFORMS(SCHEME_INT_VAL(expr)))
|
||||
return SCHEME_LOCAL_TYPE_FIXNUM;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int scheme_expr_produces_local_type(Scheme_Object *expr)
|
||||
{
|
||||
return expr_produces_local_type(expr, 10);
|
||||
}
|
||||
|
||||
static Scheme_Object *local_type_to_predicate(int t)
|
||||
{
|
||||
switch (t) {
|
||||
|
@ -2471,6 +2373,24 @@ static Scheme_Object *local_type_to_predicate(int t)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static int predicate_to_local_type(Scheme_Object *pred)
|
||||
{
|
||||
if (!pred)
|
||||
return 0;
|
||||
if (SAME_OBJ(scheme_flonum_p_proc, pred))
|
||||
return SCHEME_LOCAL_TYPE_FLONUM;
|
||||
if (SAME_OBJ(scheme_fixnum_p_proc, pred))
|
||||
return SCHEME_LOCAL_TYPE_FIXNUM;
|
||||
if (SAME_OBJ(scheme_extflonum_p_proc, pred))
|
||||
return SCHEME_LOCAL_TYPE_EXTFLONUM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int scheme_expr_produces_local_type(Scheme_Object *expr)
|
||||
{
|
||||
return predicate_to_local_type(expr_implies_predicate(expr, NULL, 0, 10));
|
||||
}
|
||||
|
||||
static Scheme_Object *rator_implies_predicate(Scheme_Object *rator, int argc)
|
||||
{
|
||||
if (SCHEME_PRIMP(rator)) {
|
||||
|
@ -2513,10 +2433,11 @@ static Scheme_Object *rator_implies_predicate(Scheme_Object *rator, int argc)
|
|||
}
|
||||
|
||||
static Scheme_Object *expr_implies_predicate(Scheme_Object *expr, Optimize_Info *info, int delta, int fuel)
|
||||
/* can be called by the JIT with info = NULL;
|
||||
in that case, beware that the validator must be
|
||||
able to reconstruct the result in a shallow way, so don't
|
||||
make the result of a function call depend on its arguments */
|
||||
{
|
||||
Scheme_Object *rator = NULL;
|
||||
int argc = 0;
|
||||
|
||||
if (fuel <= 0)
|
||||
return NULL;
|
||||
|
||||
|
@ -2528,6 +2449,10 @@ static Scheme_Object *expr_implies_predicate(Scheme_Object *expr, Optimize_Info
|
|||
pos -= delta;
|
||||
if (pos < 0)
|
||||
return NULL;
|
||||
|
||||
if (!info)
|
||||
return NULL;
|
||||
|
||||
if (!optimize_is_mutated(info, pos)){
|
||||
p = optimize_get_predicate(pos, info);
|
||||
if (p)
|
||||
|
@ -2540,16 +2465,36 @@ static Scheme_Object *expr_implies_predicate(Scheme_Object *expr, Optimize_Info
|
|||
}
|
||||
break;
|
||||
case scheme_application2_type:
|
||||
rator = ((Scheme_App2_Rec *)expr)->rator;
|
||||
argc = 1;
|
||||
{
|
||||
Scheme_App2_Rec *app = (Scheme_App2_Rec *)expr;
|
||||
|
||||
return rator_implies_predicate(app->rator, 1);
|
||||
}
|
||||
break;
|
||||
case scheme_application3_type:
|
||||
rator = ((Scheme_App3_Rec *)expr)->rator;
|
||||
argc = 2;
|
||||
{
|
||||
Scheme_App3_Rec *app = (Scheme_App3_Rec *)expr;
|
||||
if (SCHEME_PRIMP(app->rator)
|
||||
&& (SCHEME_PRIM_PROC_OPT_FLAGS(app->rator) & SCHEME_PRIM_IS_BINARY_INLINED)
|
||||
&& IS_NAMED_PRIM(app->rator, "bitwise-and")) {
|
||||
/* Assume that a fixnum argument to bitwise-and will never get lost,
|
||||
and so the validator will be able to confirm that a `bitwise-and`
|
||||
combination produces a fixnum. */
|
||||
if ((SCHEME_INTP(app->rand1)
|
||||
&& IN_FIXNUM_RANGE_ON_ALL_PLATFORMS(SCHEME_INT_VAL(app->rand1)))
|
||||
|| (SCHEME_INTP(app->rand2)
|
||||
&& IN_FIXNUM_RANGE_ON_ALL_PLATFORMS(SCHEME_INT_VAL(app->rand2))))
|
||||
return scheme_fixnum_p_proc;
|
||||
}
|
||||
return rator_implies_predicate(app->rator, 2);
|
||||
}
|
||||
break;
|
||||
case scheme_application_type:
|
||||
argc = ((Scheme_App_Rec *)expr)->num_args;
|
||||
rator = ((Scheme_App_Rec *)expr)->args[0];
|
||||
{
|
||||
Scheme_App_Rec *app = (Scheme_App_Rec *)expr;
|
||||
|
||||
return rator_implies_predicate(app->args[0], app->num_args);
|
||||
}
|
||||
break;
|
||||
case scheme_compiled_unclosed_procedure_type:
|
||||
return scheme_procedure_p_proc;
|
||||
|
@ -2590,6 +2535,12 @@ static Scheme_Object *expr_implies_predicate(Scheme_Object *expr, Optimize_Info
|
|||
return expr_implies_predicate(expr, info, delta, fuel-1);
|
||||
}
|
||||
break;
|
||||
case scheme_begin0_sequence_type:
|
||||
{
|
||||
Scheme_Sequence *seq = (Scheme_Sequence *)expr;
|
||||
|
||||
return expr_implies_predicate(seq->array[0], info, delta, fuel-1);
|
||||
}
|
||||
case scheme_pair_type:
|
||||
return scheme_pair_p_proc;
|
||||
break;
|
||||
|
@ -2626,12 +2577,12 @@ static Scheme_Object *expr_implies_predicate(Scheme_Object *expr, Optimize_Info
|
|||
return scheme_not_prim;
|
||||
}
|
||||
|
||||
if (rator)
|
||||
return rator_implies_predicate(rator, argc);
|
||||
|
||||
{
|
||||
/* These tests are slower, so put them at the end */
|
||||
int flags, sub_context = 0;
|
||||
if (!info)
|
||||
return NULL;
|
||||
|
||||
if (lookup_constant_proc(info, expr, delta)
|
||||
|| optimize_for_inline(info, expr, 1, NULL, NULL, NULL, &flags, sub_context, 1, delta)){
|
||||
return scheme_procedure_p_proc;
|
||||
|
|
Loading…
Reference in New Issue
Block a user