From 36d563d7ce62df5d63eab390a5c934578e6d7ec6 Mon Sep 17 00:00:00 2001 From: Matthew Flatt Date: Fri, 22 Dec 2017 21:30:29 -0700 Subject: [PATCH] improve JIT impersonator property predicate/accessor fast path Faster for the case that an impersonator has a small number of impersonator properties. To enable hand-coded search, a small number of properties are now kept in a vector instead of a hash tree. --- .../tests/racket/chaperone.rktl | 18 +- racket/src/racket/src/fun.c | 13 +- racket/src/racket/src/jitcommon.c | 62 ++++++- racket/src/racket/src/list.c | 4 +- racket/src/racket/src/schpriv.h | 6 +- racket/src/racket/src/sema.c | 2 +- racket/src/racket/src/struct.c | 160 ++++++++++++++---- racket/src/racket/src/vector.c | 2 +- 8 files changed, 216 insertions(+), 51 deletions(-) diff --git a/pkgs/racket-test-core/tests/racket/chaperone.rktl b/pkgs/racket-test-core/tests/racket/chaperone.rktl index e2b4a9f740..77b46bbe58 100644 --- a/pkgs/racket-test-core/tests/racket/chaperone.rktl +++ b/pkgs/racket-test-core/tests/racket/chaperone.rktl @@ -281,7 +281,23 @@ (err/rt-test (get1 v7) handler) (err/rt-test (get1 v8) handler) (err/rt-test (get1 v9) handler) - (err/rt-test (get1 v10) handler))) + (err/rt-test (get1 v10) handler) + + ;; Make sure it works to have lots of impersontaors: + (let loop ([v v1] [i 100] [preds null] [sels null] [vals null]) + (unless (zero? i) + (for ([pred (in-list preds)] + [sel (in-list sels)] + [val (in-list vals)]) + (test #t pred v) + (test val sel v) + (test val sel v 'no)) + (define-values (p has get) (make-impersonator-property 'p)) + (loop (chaperone-vector v #f #f p i) + (sub1 i) + (cons has preds) + (cons get sels) + (cons i vals)))))) ;; check property-only chaperones diff --git a/racket/src/racket/src/fun.c b/racket/src/racket/src/fun.c index a8de261f6c..c85fb12a8a 100644 --- a/racket/src/racket/src/fun.c +++ b/racket/src/racket/src/fun.c @@ -3535,7 +3535,7 @@ static Scheme_Object *do_chaperone_procedure(const char *name, const char *whati { Scheme_Chaperone *px, *px2; Scheme_Object *val = argv[0], *orig, *naya, *r, *app_mark; - Scheme_Hash_Tree *props; + Scheme_Object *props; if (SCHEME_CHAPERONEP(val)) val = SCHEME_CHAPERONE_VAL(val); @@ -3569,13 +3569,10 @@ static Scheme_Object *do_chaperone_procedure(const char *name, const char *whati props = scheme_parse_chaperone_props(name, 2, argc, argv); if (props) { - app_mark = scheme_hash_tree_get(props, scheme_app_mark_impersonator_property); + app_mark = scheme_chaperone_props_get(props, scheme_app_mark_impersonator_property); if (app_mark) { /* don't need to keep the property */ - if (props->count == 1) - props = NULL; - else - props = scheme_hash_tree_set(props, scheme_app_mark_impersonator_property, NULL); + props = scheme_chaperone_props_remove(props, scheme_app_mark_impersonator_property); } else app_mark = scheme_false; } else @@ -4555,7 +4552,7 @@ Scheme_Object *do_chaperone_continuation_mark_key (const char *name, int is_impe Scheme_Chaperone *px; Scheme_Object *val = argv[0]; Scheme_Object *redirects; - Scheme_Hash_Tree *props; + Scheme_Object *props; if (SCHEME_CHAPERONEP(val)) val = SCHEME_CHAPERONE_VAL(val); @@ -6495,7 +6492,7 @@ Scheme_Object *do_chaperone_prompt_tag (const char *name, int is_impersonator, i Scheme_Chaperone *px; Scheme_Object *val = argv[0]; Scheme_Object *redirects; - Scheme_Hash_Tree *props; + Scheme_Object *props; int ppos; if (SCHEME_CHAPERONEP(val)) diff --git a/racket/src/racket/src/jitcommon.c b/racket/src/racket/src/jitcommon.c index bb9049f190..aff91c7aff 100644 --- a/racket/src/racket/src/jitcommon.c +++ b/racket/src/racket/src/jitcommon.c @@ -2072,7 +2072,7 @@ static int common4b(mz_jit_state *jitter, void *_data) for (i = 0; i < 3; i++) { for (ii = 0; ii < 3; ii++) { /* single, multi, or tail */ void *code; - GC_CAN_IGNORE jit_insn *ref, *ref2, *ref3, *ref4, *refno, *refslow, *refloop; + GC_CAN_IGNORE jit_insn *ref, *ref2, *ref3, *ref4, *ref5, *refno, *refslow, *refloop, *refchap; int prim_other_type; code = jit_get_ip(); @@ -2158,13 +2158,15 @@ static int common4b(mz_jit_state *jitter, void *_data) jit_ldr_p(JIT_V1, JIT_RUNSTACK); /* If the failure argument is not a procedure, we can return it directly, otherwise take slow path. */ - jit_ldxi_s(JIT_R2, JIT_V1, &((Scheme_Object *)0x0)->type); __START_INNER_TINY__(1); + ref5 = jit_bmsi_ul(jit_forward(), JIT_V1, 0x1); + jit_ldxi_s(JIT_R2, JIT_V1, &((Scheme_Object *)0x0)->type); ref4 = jit_blti_i(jit_forward(), JIT_R2, scheme_prim_type); __END_INNER_TINY__(1); (void)jit_blei_i(refslow, JIT_R2, scheme_proc_chaperone_type); __START_INNER_TINY__(1); mz_patch_branch(ref4); + mz_patch_branch(ref5); __END_INNER_TINY__(1); jit_movr_p(JIT_R0, JIT_V1); jit_addi_p(JIT_RUNSTACK, JIT_RUNSTACK, WORDS_TO_BYTES(1)); @@ -2173,6 +2175,58 @@ static int common4b(mz_jit_state *jitter, void *_data) } else refno = refslow; + { + /* Chaperone case: if we're looking for an impersonator property, + maybe we can find it here; otherwise, take the slow path. */ + refchap = jit_get_ip(); + + jit_ldxi_p(JIT_V1, JIT_R0, &((Scheme_Primitive_Closure *)0x0)->val); + (void)mz_bnei_t(refslow, JIT_V1, scheme_chaperone_property_type, JIT_R2); /* slow path if not impersonator property */ + jit_ldxi_p(JIT_R2, JIT_R1, &((Scheme_Chaperone *)0x0)->props); /* check chaperone's props */ + (void)jit_beqi_p(refslow, JIT_R2, NULL); + (void)mz_bnei_t(refslow, JIT_R2, scheme_vector_type, JIT_V1); /* slow path if not represented a vector */ + CHECK_LIMIT(); + + /* Search a vector for the property: */ + (void)jit_ldxi_l(JIT_V1, JIT_R2, &SCHEME_VEC_SIZE(0x0)); /* get vector size */ + jit_lshi_ul(JIT_V1, JIT_V1, JIT_LOG_WORD_SIZE); /* convert to bytes */ + jit_addi_l(JIT_V1, JIT_V1, (int)&SCHEME_VEC_ELS(0x0)); /* bytes at offset */ + + refloop = jit_get_ip(); + (void)jit_beqi_l(refslow, JIT_V1, (int)&SCHEME_VEC_ELS(0x0)); /* index at 0 => not found, so slow path */ + + jit_subi_l(JIT_V1, JIT_V1, (2 * JIT_WORD_SIZE)); /* step back by two words for key & value */ + mz_set_local_p(JIT_V1, JIT_LOCAL3); /* save current index, because we need the register */ + jit_ldxi_p(JIT_R2, JIT_R1, &((Scheme_Chaperone *)0x0)->props); /* get vector again */ + jit_ldxr_p(JIT_R2, JIT_R2, JIT_V1); /* load a key from the vector */ + jit_ldxi_p(JIT_V1, JIT_R0, &((Scheme_Primitive_Closure *)0x0)->val); /* get the property again */ + CHECK_LIMIT(); + __START_INNER_TINY__(1); + ref5 = jit_beqr_p(jit_forward(), JIT_R2, JIT_V1); /* key matches property? */ + __END_INNER_TINY__(1); + CHECK_LIMIT(); + + mz_get_local_p(JIT_V1, JIT_LOCAL3); /* no match, so reload index and recur */ + (void)jit_jmpi(refloop); + + __START_INNER_TINY__(1); + mz_patch_branch(ref5); + __END_INNER_TINY__(1); + /* found match, so return #t or extract and return the value */ + if (i == 2) { + (void)jit_movi_p(JIT_R0, scheme_true); + } else { + mz_get_local_p(JIT_V1, JIT_LOCAL3); /* reload matching index */ + jit_addi_l(JIT_V1, JIT_V1, JIT_WORD_SIZE); /* up by one word, to get value insteda of key */ + jit_ldxi_p(JIT_R2, JIT_R1, &((Scheme_Chaperone *)0x0)->props); /* get vector again */ + jit_ldxr_p(JIT_R0, JIT_R2, JIT_V1); /* extract value */ + } + if (i == 1) + jit_addi_p(JIT_RUNSTACK, JIT_RUNSTACK, WORDS_TO_BYTES(1)); + mz_epilog(JIT_V1); /* return */ + CHECK_LIMIT(); + } + /* Continue trying fast path: check proc */ mz_patch_branch(ref); (void)mz_bnei_t(refslow, JIT_R0, scheme_prim_type, JIT_R2); @@ -2187,9 +2241,9 @@ static int common4b(mz_jit_state *jitter, void *_data) __START_INNER_TINY__(1); ref2 = jit_beqi_i(jit_forward(), JIT_R2, scheme_structure_type); __END_INNER_TINY__(1); + (void)jit_beqi_i(refchap, JIT_R2, scheme_proc_chaperone_type); + (void)jit_beqi_i(refchap, JIT_R2, scheme_chaperone_type); if (i != 0) { /* for i == 0 mode, `refno` is already `refslow` */ - (void)jit_beqi_i(refslow, JIT_R2, scheme_proc_chaperone_type); - (void)jit_beqi_i(refslow, JIT_R2, scheme_chaperone_type); (void)jit_beqi_i(refslow, JIT_R2, scheme_struct_type_type); } (void)jit_bnei_i(refno, JIT_R2, scheme_proc_struct_type); diff --git a/racket/src/racket/src/list.c b/racket/src/racket/src/list.c index d73f58c804..7881bc9d53 100644 --- a/racket/src/racket/src/list.c +++ b/racket/src/racket/src/list.c @@ -1966,7 +1966,7 @@ static Scheme_Object *do_chaperone_box(const char *name, int is_impersonator, in Scheme_Chaperone *px; Scheme_Object *val = argv[0]; Scheme_Object *redirects; - Scheme_Hash_Tree *props; + Scheme_Object *props; if (SCHEME_CHAPERONEP(val)) val = SCHEME_CHAPERONE_VAL(val); @@ -3177,7 +3177,7 @@ static Scheme_Object *do_chaperone_hash(const char *name, int is_impersonator, i Scheme_Chaperone *px; Scheme_Object *val = argv[0]; Scheme_Object *redirects, *clear, *equal_key_wrap; - Scheme_Hash_Tree *props; + Scheme_Object *props; int start_props = 5; if (SCHEME_CHAPERONEP(val)) diff --git a/racket/src/racket/src/schpriv.h b/racket/src/racket/src/schpriv.h index b782fb11d6..cc9d850ff5 100644 --- a/racket/src/racket/src/schpriv.h +++ b/racket/src/racket/src/schpriv.h @@ -1159,7 +1159,7 @@ typedef struct Scheme_Chaperone { Scheme_Inclhash_Object iso; /* 0x1 => impersonator, rather than a checking chaperone */ Scheme_Object *val; /* root object */ Scheme_Object *prev; /* immediately chaperoned object */ - Scheme_Hash_Tree *props; + Scheme_Object *props; /* NULL, a vector, or a hash tree */ Scheme_Object *redirects; /* specific to the type of chaperone and root object */ } Scheme_Chaperone; @@ -1218,7 +1218,9 @@ void scheme_chaperone_vector_set(Scheme_Object *o, int i, Scheme_Object *v); Scheme_Object *scheme_apply_chaperone(Scheme_Object *o, int argc, Scheme_Object **argv, Scheme_Object *auto_val, int checks); -Scheme_Hash_Tree *scheme_parse_chaperone_props(const char *who, int start_at, int argc, Scheme_Object **argv); +Scheme_Object *scheme_parse_chaperone_props(const char *who, int start_at, int argc, Scheme_Object **argv); +Scheme_Object *scheme_chaperone_props_get(Scheme_Object *props, Scheme_Object *prop); +Scheme_Object *scheme_chaperone_props_remove(Scheme_Object *props, Scheme_Object *prop); Scheme_Object *scheme_chaperone_hash_get(Scheme_Object *table, Scheme_Object *key); Scheme_Object *scheme_chaperone_hash_traversal_get(Scheme_Object *table, Scheme_Object *key, Scheme_Object **alt_key); diff --git a/racket/src/racket/src/sema.c b/racket/src/racket/src/sema.c index 8d1ec5190d..21ba281003 100644 --- a/racket/src/racket/src/sema.c +++ b/racket/src/racket/src/sema.c @@ -1202,7 +1202,7 @@ Scheme_Object *do_chaperone_channel(const char *name, int is_impersonator, int a Scheme_Chaperone *px; Scheme_Object *val = argv[0]; Scheme_Object *evt; - Scheme_Hash_Tree *props; + Scheme_Object *props; if (SCHEME_CHAPERONEP(val)) val = SCHEME_CHAPERONE_VAL(val); diff --git a/racket/src/racket/src/struct.c b/racket/src/racket/src/struct.c index 5903090468..eccfa4b22e 100644 --- a/racket/src/racket/src/struct.c +++ b/racket/src/racket/src/struct.c @@ -1059,6 +1059,43 @@ static Scheme_Object *current_code_inspector(int argc, Scheme_Object *argv[]) /* properties */ /*========================================================================*/ +Scheme_Object *scheme_chaperone_props_get(Scheme_Object *props, Scheme_Object *prop) +{ + if (!props) + return NULL; + else if (SCHEME_VECTORP(props)) { + int i; + for (i = SCHEME_VEC_SIZE(props); i > 0; ) { + i -= 2; + if (SAME_OBJ(SCHEME_VEC_ELS(props)[i], prop)) + return SCHEME_VEC_ELS(props)[i+1]; + } + return NULL; + } else + return (Scheme_Object *)scheme_hash_tree_get((Scheme_Hash_Tree *)props, prop); +} + +Scheme_Object *scheme_chaperone_props_remove(Scheme_Object *props, Scheme_Object *prop) +/* assumes that `prop` is currently set in `props` */ +{ + if (SCHEME_VECTORP(props)) { + Scheme_Object *vec; + int i, j; + j = SCHEME_VEC_SIZE(props); + if (j == 2) return NULL; + vec = scheme_make_vector(j - 2, NULL); + for (i = SCHEME_VEC_SIZE(props), j = 0; i > 0; ) { + i -= 2; + if (!SAME_OBJ(SCHEME_VEC_ELS(props)[i], prop)) { + SCHEME_VEC_ELS(vec)[j++] = SCHEME_VEC_ELS(props)[i]; + SCHEME_VEC_ELS(vec)[j++] = SCHEME_VEC_ELS(props)[i+1]; + } + } + return vec; + } else + return (Scheme_Object *)scheme_hash_tree_set((Scheme_Hash_Tree *)props, prop, NULL); +} + static Scheme_Object *prop_pred(int argc, Scheme_Object **args, Scheme_Object *prim) { Scheme_Struct_Type *stype; @@ -1071,10 +1108,7 @@ static Scheme_Object *prop_pred(int argc, Scheme_Object **args, Scheme_Object *p if (SCHEME_CHAPERONEP(v)) { /* Check for property at chaperone level: */ px = (Scheme_Chaperone *)v; - if (px->props) - v = scheme_hash_tree_get(px->props, prop); - else - v = NULL; + v = scheme_chaperone_props_get(px->props, prop); if (v) return scheme_true; v = px->val; @@ -1187,11 +1221,9 @@ static Scheme_Object *do_chaperone_prop_accessor(const char *who, Scheme_Object Scheme_Object *v; Scheme_Hash_Tree *ht; - if (px->props) { - v = scheme_hash_tree_get(px->props, prop); - if (v) - return v; - } + v = scheme_chaperone_props_get(px->props, prop); + if (v) + return v; if (!SCHEME_REDIRECTS_STRUCTP(px->redirects) || SCHEME_FALSEP(SCHEME_VEC_ELS(px->redirects)[0])) @@ -4056,7 +4088,7 @@ Scheme_Object *scheme_do_chaperone_evt(const char *name, int is_impersonator, in { Scheme_Chaperone *px; Scheme_Object *o, *val, *a[1]; - Scheme_Hash_Tree *props; + Scheme_Object *props; val = argv[0]; if (SCHEME_CHAPERONEP(val)) @@ -6212,12 +6244,12 @@ static Scheme_Object *do_chaperone_struct(const char *name, int is_impersonator, { Scheme_Chaperone *px; Scheme_Struct_Type *stype, *st; - Scheme_Object *val = argv[0], *proc; + Scheme_Object *val = argv[0], *proc, *props = NULL; Scheme_Object *redirects, *prop, *si_chaperone = scheme_false; Scheme_Object *a[1], *inspector, *getter_positions = scheme_null; int i, offset, arity, non_applicable_op, repeat_op; const char *kind; - Scheme_Hash_Tree *props = NULL, *red_props = NULL, *empty_red_props = NULL, *setter_positions = NULL; + Scheme_Hash_Tree *red_props = NULL, *empty_red_props = NULL, *setter_positions = NULL; intptr_t field_pos; int empty_si_chaperone = 0, *empty_redirects = NULL, has_redirect = 0, witnessed = 0; @@ -6555,7 +6587,7 @@ Scheme_Object *scheme_chaperone_not_undefined (Scheme_Object *orig_val) { Scheme_Chaperone *px; Scheme_Object *val, *redirects; - Scheme_Hash_Tree *props; + Scheme_Object *props; val = orig_val; @@ -6589,7 +6621,7 @@ static Scheme_Object *do_chaperone_struct_type(const char *name, int is_imperson Scheme_Chaperone *px; Scheme_Object *val = argv[0]; Scheme_Object *redirects; - Scheme_Hash_Tree *props; + Scheme_Object *props; int arity; if (SCHEME_CHAPERONEP(val)) @@ -6635,35 +6667,99 @@ static Scheme_Object *chaperone_struct_type(int argc, Scheme_Object **argv) return do_chaperone_struct_type("chaperone-struct-type", 0, argc, argv); } -Scheme_Hash_Tree *scheme_parse_chaperone_props(const char *who, int start_at, int argc, Scheme_Object **argv) +#define PROPS_MAX_VECTOR_SIZE 5 + +Scheme_Object *scheme_parse_chaperone_props(const char *who, int start_at, int argc, Scheme_Object **argv) { - Scheme_Hash_Tree *ht; - Scheme_Object *v; + Scheme_Object *v, *props; + int pos; if (SCHEME_CHAPERONEP(argv[0])) - ht = ((Scheme_Chaperone *)argv[0])->props; + props = ((Scheme_Chaperone *)argv[0])->props; else - ht = NULL; + props = NULL; - while (start_at < argc) { - v = argv[start_at]; - if (!SAME_TYPE(SCHEME_TYPE(v), scheme_chaperone_property_type)) - scheme_wrong_contract(who, "impersonator-property?", start_at, argc, argv); + if (start_at < argc) { + /* Check */ + for (pos = start_at; pos < argc; pos += 2) { + v = argv[pos]; + if (!SAME_TYPE(SCHEME_TYPE(v), scheme_chaperone_property_type)) + scheme_wrong_contract(who, "impersonator-property?", pos, argc, argv); - if (start_at + 1 >= argc) - scheme_contract_error(who, - "missing value after chaperone property", - "chaperone property", 1, v, - NULL); + if (pos + 1 >= argc) + scheme_contract_error(who, + "missing value after chaperone property", + "chaperone property", 1, v, + NULL); + } - if (!ht) + /* Prepare to add */ + if (props && SCHEME_VECTORP(props) + && (((argc - start_at) + SCHEME_VEC_SIZE(props)) > (2 * PROPS_MAX_VECTOR_SIZE))) { + /* Convert vector to a hash tree */ + Scheme_Hash_Tree *ht; + int i; ht = scheme_make_hash_tree(SCHEME_hashtr_eq); - ht = scheme_hash_tree_set(ht, v, argv[start_at + 1]); + for (i = SCHEME_VEC_SIZE(props); i > 0; ) { + i -= 2; + ht = scheme_hash_tree_set(ht, SCHEME_VEC_ELS(props)[i], SCHEME_VEC_ELS(props)[i+1]); + } + props = (Scheme_Object *)ht; + } - start_at += 2; + if (!props || SCHEME_VECTORP(props)) { + /* Keep as vector, and start by counting new distinct entries */ + int count = 0, i, len = (props ? SCHEME_VEC_SIZE(props) : 0); + + for (pos = start_at; pos < argc; pos += 2) { + v = argv[pos]; + if (props) { + for (i = 0; i < len; i += 2) { + if (SAME_OBJ(v, SCHEME_VEC_ELS(props)[i])) + break; + } + } else + i = 0; + if (i >= len) { + for (i = start_at; i < pos; i += 2) { + if (SAME_OBJ(v, argv[i])) + break; + } + if (i >= pos) + count++; + } + } + + if (props) { + /* Copy vector, possibly making it larger */ + Scheme_Object *old_props = props; + props = scheme_make_vector(SCHEME_VEC_SIZE(old_props) + 2 * count, NULL); + memcpy(SCHEME_VEC_ELS(props), SCHEME_VEC_ELS(old_props), sizeof(Scheme_Object *)*SCHEME_VEC_SIZE(old_props)); + } else { + props = scheme_make_vector(2 * count, NULL); + } + len = SCHEME_VEC_SIZE(props); + + /* Fill in vector */ + for (pos = start_at; pos < argc; pos += 2) { + v = argv[pos]; + for (i = 0; i < len; i += 2) { + if (!SCHEME_VEC_ELS(props)[i] || SAME_OBJ(v, SCHEME_VEC_ELS(props)[i])) { + SCHEME_VEC_ELS(props)[i] = v; + SCHEME_VEC_ELS(props)[i+1] = argv[pos+1]; + break; + } + } + } + } else { + /* Add to hash tree */ + for (pos = start_at; pos < argc; pos += 2) { + props = (Scheme_Object *)scheme_hash_tree_set((Scheme_Hash_Tree *)props, argv[pos], argv[pos+1]); + } + } } - return ht; + return props; } /**********************************************************************/ diff --git a/racket/src/racket/src/vector.c b/racket/src/racket/src/vector.c index 7b098890ac..0cd0199115 100644 --- a/racket/src/racket/src/vector.c +++ b/racket/src/racket/src/vector.c @@ -964,7 +964,7 @@ static Scheme_Object *do_chaperone_vector(const char *name, int is_impersonator, Scheme_Chaperone *px; Scheme_Object *val = argv[0]; Scheme_Object *redirects; - Scheme_Hash_Tree *props; + Scheme_Object *props; if (SCHEME_CHAPERONEP(val)) { val = SCHEME_CHAPERONE_VAL(val);