MIRROR: javascript for ๐Ÿœ's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

reduce sv_func memory from 447 bytes to 200 bytes with sidecar

+166 -85
+153 -71
include/silver/engine.h
··· 109 109 bool sv_lookup_srcspan(sv_func_t *func, int bc_offset, uint32_t *src_off, uint32_t *src_end); 110 110 111 111 #ifdef ANT_JIT 112 + 113 + #define SV_TFB_CTOR_PROP_BINS 17 114 + #define SV_TFB_CTOR_PROP_OVERFLOW_FROM (SV_TFB_CTOR_PROP_BINS - 1) 115 + 112 116 typedef struct { 113 117 uint16_t bc_off; 114 118 uint8_t miss_count; 115 119 uint8_t disabled; 116 120 sv_func_t *target; 117 121 } sv_call_target_fb_t; 122 + 123 + typedef struct { 124 + uint64_t samples; 125 + uint64_t hist[SV_TFB_CTOR_PROP_BINS]; 126 + uint8_t inobj_limit; 127 + uint8_t inobj_frozen; 128 + } sv_ctor_prop_fb_t; 129 + 130 + typedef struct { 131 + uint8_t *type_feedback; 132 + sv_ctor_prop_fb_t ctor_prop_fb; 133 + } sv_func_sidecar_t; 118 134 #endif 119 135 120 136 struct sv_func { 121 137 uint8_t *code; 122 - int code_len; 123 - 124 138 ant_value_t *constants; 125 - int const_count; 126 139 127 140 struct sv_func **child_funcs; 128 - int child_func_count; 129 - 130 141 uint32_t *gc_const_slots; 131 - int gc_const_slot_count; 132 142 133 143 sv_atom_t *atoms; 134 - int atom_count; 135 - 136 144 sv_ic_entry_t *ic_slots; 137 - uint16_t ic_count; 138 - 139 145 sv_obj_site_cache_t *obj_sites; 140 - uint16_t obj_site_count; 146 + sv_upval_desc_t *upval_descs; 147 + sv_type_info_t *local_types; 141 148 142 - sv_upval_desc_t *upval_descs; 149 + const char *name; 150 + const char *filename; 151 + sv_srcpos_t *srcpos; 152 + const char *source; 153 + 154 + #ifdef ANT_JIT 155 + void *jit_code; 156 + uint8_t *type_feedback; 157 + uint8_t *local_type_feedback; 158 + sv_call_target_fb_t *call_target_fb; 159 + #endif 160 + 161 + uint64_t gc_epoch; 162 + 163 + int code_len; 164 + int const_count; 165 + int child_func_count; 166 + int gc_const_slot_count; 167 + int atom_count; 143 168 int max_locals; 144 169 int max_stack; 145 - 146 - sv_type_info_t *local_types; 147 170 int local_type_count; 148 - 149 171 int param_count; 150 172 int upvalue_count; 151 - 173 + int srcpos_count; 174 + int source_line; 175 + int source_len; 176 + int source_start; 177 + int source_end; 178 + 179 + uint16_t ic_count; 180 + uint16_t obj_site_count; 181 + 152 182 bool is_strict; 153 183 bool is_arrow; 154 184 bool is_async; ··· 157 187 bool is_method; 158 188 bool is_static; 159 189 bool is_tla; 160 - uint64_t gc_epoch; 161 - 162 - const char *name; 163 - const char *filename; 164 - 165 - sv_srcpos_t *srcpos; 166 - int srcpos_count; 167 - int source_line; 168 - 169 - const char *source; 170 - int source_len; 171 - int source_start; 172 - int source_end; 173 - 190 + 174 191 #ifdef ANT_JIT 175 - void *jit_code; 176 - 177 192 uint32_t call_count; 178 193 uint32_t back_edge_count; 179 - 180 - bool jit_compile_failed; 181 - bool jit_compiling; 182 - 183 - uint8_t jit_bailout_count; 184 194 uint32_t jit_bailout_tfb_ver; 185 195 uint32_t tfb_version; 186 196 uint32_t jit_compiled_tfb_ver; 187 - uint8_t *type_feedback; 188 - uint8_t *local_type_feedback; 189 - uint64_t ctor_prop_samples; 190 - uint64_t ctor_prop_hist[17]; 191 - uint8_t ctor_inobj_limit; 192 - uint8_t ctor_inobj_frozen; 193 197 194 - sv_call_target_fb_t *call_target_fb; 198 + uint8_t jit_bailout_count; 195 199 uint8_t call_target_fb_count; 200 + 201 + bool jit_compile_failed; 202 + bool jit_compiling; 196 203 #endif 197 204 }; 198 205 ··· 847 854 #define SV_TFB_BOOL (1 << 2) 848 855 #define SV_TFB_OTHER (1 << 3) 849 856 850 - #define SV_TFB_CTOR_PROP_BINS 17 851 - #define SV_TFB_CTOR_PROP_OVERFLOW_FROM (SV_TFB_CTOR_PROP_BINS - 1) 852 857 #define SV_TFB_INOBJ_SLACK_ALLOCATIONS 32 853 858 #define SV_TFB_INOBJ_P90_NUMERATOR 9 854 859 #define SV_TFB_INOBJ_P90_DENOMINATOR 10 ··· 955 960 return SV_TFB_OTHER; 956 961 } 957 962 963 + static inline bool sv_func_has_sidecar(const sv_func_t *func) { 964 + return func && (((uintptr_t)func->type_feedback & ant_sidecar) != 0); 965 + } 966 + 967 + static inline sv_func_sidecar_t *sv_func_sidecar(const sv_func_t *func) { 968 + if (!sv_func_has_sidecar(func)) return NULL; 969 + return (sv_func_sidecar_t *)((uintptr_t)func->type_feedback & ~ant_sidecar); 970 + } 971 + 972 + static inline uint8_t *sv_func_type_feedback(const sv_func_t *func) { 973 + if (!func) return NULL; 974 + sv_func_sidecar_t *sidecar = sv_func_sidecar(func); 975 + return sidecar ? sidecar->type_feedback : func->type_feedback; 976 + } 977 + 978 + static inline sv_func_sidecar_t *sv_func_ensure_sidecar(sv_func_t *func) { 979 + if (!func) return NULL; 980 + 981 + sv_func_sidecar_t *sidecar = sv_func_sidecar(func); 982 + if (sidecar) return sidecar; 983 + 984 + sidecar = (sv_func_sidecar_t *)calloc(1, sizeof(*sidecar)); 985 + if (!sidecar) return NULL; 986 + 987 + sidecar->type_feedback = func->type_feedback; 988 + func->type_feedback = (uint8_t *)((uintptr_t)sidecar | ant_sidecar); 989 + 990 + return sidecar; 991 + } 992 + 958 993 static inline void sv_tfb_record2(sv_func_t *func, uint8_t *ip, ant_value_t l, ant_value_t r) { 959 - if (func->type_feedback) { 994 + uint8_t *type_feedback = sv_func_type_feedback(func); 995 + 996 + if (type_feedback) { 960 997 int off = (int)(ip - func->code); 961 - uint8_t old = func->type_feedback[off]; 998 + 999 + uint8_t old = type_feedback[off]; 962 1000 uint8_t neu = old | sv_tfb_classify(l) | sv_tfb_classify(r); 963 - if (neu != old) { func->type_feedback[off] = neu; func->tfb_version++; } 964 - }} 1001 + 1002 + if (neu != old) { 1003 + type_feedback[off] = neu; 1004 + func->tfb_version++; 1005 + }} 1006 + } 965 1007 966 1008 static inline void sv_tfb_record1(sv_func_t *func, uint8_t *ip, ant_value_t v) { 967 - if (func->type_feedback) { 1009 + uint8_t *type_feedback = sv_func_type_feedback(func); 1010 + if (type_feedback) { 968 1011 int off = (int)(ip - func->code); 969 - uint8_t old = func->type_feedback[off]; 1012 + 1013 + uint8_t old = type_feedback[off]; 970 1014 uint8_t neu = old | sv_tfb_classify(v); 971 - if (neu != old) { func->type_feedback[off] = neu; func->tfb_version++; } 972 - }} 1015 + 1016 + if (neu != old) { 1017 + type_feedback[off] = neu; 1018 + func->tfb_version++; 1019 + }} 1020 + } 973 1021 974 1022 static inline void sv_tfb_ensure(sv_func_t *fn) { 975 - if (!fn->type_feedback && fn->code_len > 0) 976 - fn->type_feedback = calloc((size_t)fn->code_len, 1); 1023 + if (!sv_func_type_feedback(fn) && fn->code_len > 0) { 1024 + uint8_t *type_feedback = calloc((size_t)fn->code_len, 1); 1025 + if (sv_func_has_sidecar(fn)) sv_func_sidecar(fn)->type_feedback = type_feedback; 1026 + else fn->type_feedback = type_feedback; 1027 + } 977 1028 if (!fn->local_type_feedback && fn->max_locals > 0) 978 1029 fn->local_type_feedback = calloc((size_t)fn->max_locals, 1); 979 1030 } ··· 991 1042 if (fb[i].miss_count >= SV_CALL_FB_MISS_DISABLE) { 992 1043 fb[i].disabled = 1; 993 1044 fb[i].target = NULL; 994 - } else { 995 - fb[i].target = callee; 996 - } 1045 + } else fb[i].target = callee; 997 1046 func->tfb_version++; 998 1047 return; 999 1048 } ··· 1032 1081 return (limit > ANT_INOBJ_MAX_SLOTS) ? (uint8_t)ANT_INOBJ_MAX_SLOTS : (uint8_t)limit; 1033 1082 } 1034 1083 1084 + static inline sv_ctor_prop_fb_t *sv_tfb_ctor_prop_fb(sv_func_t *func, bool create) { 1085 + if (!func) return NULL; 1086 + sv_func_sidecar_t *sidecar = create ? sv_func_ensure_sidecar(func) : sv_func_sidecar(func); 1087 + return sidecar ? &sidecar->ctor_prop_fb : NULL; 1088 + } 1089 + 1090 + static inline uint64_t sv_tfb_ctor_prop_samples(const sv_func_t *func) { 1091 + sv_ctor_prop_fb_t *fb = func ? sv_tfb_ctor_prop_fb((sv_func_t *)func, false) : NULL; 1092 + return fb ? fb->samples : 0; 1093 + } 1094 + 1095 + static inline uint64_t sv_tfb_ctor_prop_bin(const sv_func_t *func, uint32_t bin) { 1096 + sv_ctor_prop_fb_t *fb = func ? sv_tfb_ctor_prop_fb((sv_func_t *)func, false) : NULL; 1097 + if (!fb || bin >= SV_TFB_CTOR_PROP_BINS) return 0; 1098 + return fb->hist[bin]; 1099 + } 1100 + 1035 1101 static inline uint8_t sv_tfb_infer_inobj_limit(const sv_func_t *func, uint64_t samples) { 1036 1102 if (!func || samples == 0) return (uint8_t)ANT_INOBJ_MAX_SLOTS; 1103 + sv_ctor_prop_fb_t *fb = sv_tfb_ctor_prop_fb((sv_func_t *)func, false); 1104 + if (!fb) return (uint8_t)ANT_INOBJ_MAX_SLOTS; 1037 1105 1038 1106 uint64_t target = ( 1039 1107 (samples * SV_TFB_INOBJ_P90_NUMERATOR) ··· 1043 1111 1044 1112 uint64_t seen = 0; 1045 1113 for (uint32_t i = 0; i < SV_TFB_CTOR_PROP_BINS; i++) { 1046 - seen += func->ctor_prop_hist[i]; 1114 + seen += fb->hist[i]; 1047 1115 if (seen < target) continue; 1048 - 1049 1116 if (i >= SV_TFB_CTOR_PROP_OVERFLOW_FROM) return (uint8_t)ANT_INOBJ_MAX_SLOTS; 1050 1117 return sv_tfb_clamp_inobj_limit(i); 1051 1118 } ··· 1056 1123 static inline void sv_tfb_record_ctor_prop_count(ant_value_t ctor_func, ant_value_t instance) { 1057 1124 if (vtype(ctor_func) != T_FUNC) return; 1058 1125 if (!is_object_type(instance)) return; 1126 + 1059 1127 sv_closure_t *closure = js_func_closure(ctor_func); 1060 1128 if (!closure || !closure->func) return; 1129 + 1061 1130 ant_object_t *obj = js_obj_ptr(js_as_obj(instance)); 1062 1131 if (!obj) return; 1063 1132 1064 1133 sv_func_t *func = closure->func; 1134 + sv_ctor_prop_fb_t *fb = sv_tfb_ctor_prop_fb(func, true); 1135 + if (!fb) return; 1136 + 1065 1137 uint32_t count = obj->prop_count; 1066 1138 uint32_t bin = (count < SV_TFB_CTOR_PROP_OVERFLOW_FROM) 1067 1139 ? count 1068 1140 : SV_TFB_CTOR_PROP_OVERFLOW_FROM; 1069 - func->ctor_prop_hist[bin]++; 1070 - uint64_t samples = ++func->ctor_prop_samples; 1071 - if (!func->ctor_inobj_frozen && samples >= SV_TFB_INOBJ_SLACK_ALLOCATIONS) { 1072 - func->ctor_inobj_limit = sv_tfb_infer_inobj_limit(func, samples); 1073 - func->ctor_inobj_frozen = 1; 1141 + 1142 + fb->hist[bin]++; 1143 + uint64_t samples = ++fb->samples; 1144 + 1145 + if (!fb->inobj_frozen && samples >= SV_TFB_INOBJ_SLACK_ALLOCATIONS) { 1146 + fb->inobj_limit = sv_tfb_infer_inobj_limit(func, samples); 1147 + fb->inobj_frozen = 1; 1074 1148 } 1075 1149 } 1076 1150 ··· 1080 1154 if (!closure || !closure->func) return (uint8_t)ANT_INOBJ_MAX_SLOTS; 1081 1155 1082 1156 sv_func_t *func = closure->func; 1083 - if (!func->ctor_inobj_frozen) return (uint8_t)ANT_INOBJ_MAX_SLOTS; 1084 - return sv_tfb_clamp_inobj_limit(func->ctor_inobj_limit); 1157 + sv_ctor_prop_fb_t *fb = sv_tfb_ctor_prop_fb(func, false); 1158 + 1159 + if (!fb || !fb->inobj_frozen) return (uint8_t)ANT_INOBJ_MAX_SLOTS; 1160 + return sv_tfb_clamp_inobj_limit(fb->inobj_limit); 1085 1161 } 1086 1162 1087 1163 static inline bool sv_tfb_ctor_inobj_limit_frozen(ant_value_t ctor_func) { 1088 1164 if (vtype(ctor_func) != T_FUNC) return false; 1089 1165 sv_closure_t *closure = js_func_closure(ctor_func); 1090 1166 if (!closure || !closure->func) return false; 1091 - return closure->func->ctor_inobj_frozen != 0; 1167 + sv_ctor_prop_fb_t *fb = sv_tfb_ctor_prop_fb(closure->func, false); 1168 + return fb && fb->inobj_frozen != 0; 1092 1169 } 1093 1170 1094 1171 static inline uint32_t sv_tfb_ctor_inobj_slack_remaining(ant_value_t ctor_func) { 1095 1172 if (vtype(ctor_func) != T_FUNC) return SV_TFB_INOBJ_SLACK_ALLOCATIONS; 1096 1173 sv_closure_t *closure = js_func_closure(ctor_func); 1174 + 1097 1175 if (!closure || !closure->func) return SV_TFB_INOBJ_SLACK_ALLOCATIONS; 1098 1176 sv_func_t *func = closure->func; 1099 - if (func->ctor_inobj_frozen || func->ctor_prop_samples >= SV_TFB_INOBJ_SLACK_ALLOCATIONS) return 0; 1100 - return (uint32_t)(SV_TFB_INOBJ_SLACK_ALLOCATIONS - func->ctor_prop_samples); 1177 + sv_ctor_prop_fb_t *fb = sv_tfb_ctor_prop_fb(func, false); 1178 + 1179 + if (!fb) return SV_TFB_INOBJ_SLACK_ALLOCATIONS; 1180 + if (fb->inobj_frozen || fb->samples >= SV_TFB_INOBJ_SLACK_ALLOCATIONS) return 0; 1181 + 1182 + return (uint32_t)(SV_TFB_INOBJ_SLACK_ALLOCATIONS - fb->samples); 1101 1183 } 1102 1184 #endif 1103 1185
+3 -4
src/modules/builtin.c
··· 92 92 sv_func_t *fn = closure->func; 93 93 94 94 ant_value_t out = js_newobj(js); 95 - js_set(js, out, "samples", js_mknum((double)fn->ctor_prop_samples)); 95 + js_set(js, out, "samples", js_mknum((double)sv_tfb_ctor_prop_samples(fn))); 96 96 js_set(js, out, "overflowFrom", js_mknum((double)SV_TFB_CTOR_PROP_OVERFLOW_FROM)); 97 97 js_set(js, out, "inobjLimit", js_mknum((double)sv_tfb_ctor_inobj_limit(ctor))); 98 98 js_set(js, out, "inobjLimitFrozen", js_bool(sv_tfb_ctor_inobj_limit_frozen(ctor))); 99 99 js_set(js, out, "slackRemaining", js_mknum((double)sv_tfb_ctor_inobj_slack_remaining(ctor))); 100 100 101 101 ant_value_t bins = js_mkarr(js); 102 - for (uint32_t i = 0; i < SV_TFB_CTOR_PROP_BINS; i++) { 103 - js_arr_push(js, bins, js_mknum((double)fn->ctor_prop_hist[i])); 104 - } 102 + for (uint32_t i = 0; i < SV_TFB_CTOR_PROP_BINS; i++) 103 + js_arr_push(js, bins, js_mknum((double)sv_tfb_ctor_prop_bin(fn, i))); 105 104 js_set(js, out, "bins", bins); 106 105 107 106 if (fn->name) js_set(js, out, "name", js_mkstr(js, fn->name, strlen(fn->name)));
+2 -2
src/silver/engine.c
··· 690 690 sv_func_t *callee = closure->func; 691 691 if (!callee) return SV_JIT_RETRY_INTERP; 692 692 693 - if (caller_func && caller_func->type_feedback && caller_ip) 693 + if (caller_func && sv_func_type_feedback(caller_func) && caller_ip) 694 694 sv_tfb_record_call_target(caller_func, (int)(caller_ip - caller_func->code), callee); 695 695 696 696 if (callee->jit_code) { ··· 907 907 #ifdef ANT_JIT 908 908 #define JIT_OSR_BACK_EDGE() do { \ 909 909 if (!func->jit_compile_failed) { \ 910 - if (!func->type_feedback) sv_tfb_ensure(func); \ 910 + if (!sv_func_type_feedback(func)) sv_tfb_ensure(func); \ 911 911 if (++func->back_edge_count >= SV_JIT_OSR_THRESHOLD) { \ 912 912 ant_value_t osr_r = sv_jit_try_osr( \ 913 913 vm, js, frame, func, \
+8 -8
src/silver/swarm.c
··· 3391 3391 3392 3392 case OP_ADD: 3393 3393 case OP_ADD_NUM: { 3394 - uint8_t fb = func->type_feedback ? func->type_feedback[bc_off] : 0; 3394 + uint8_t fb = sv_func_type_feedback(func) ? sv_func_type_feedback(func)[bc_off] : 0; 3395 3395 bool force_num_only = (op == OP_ADD_NUM); 3396 3396 bool fb_num_only = force_num_only || (fb && !(fb & ~SV_TFB_NUM)); 3397 3397 bool fb_never_num = !force_num_only && fb && !(fb & SV_TFB_NUM); ··· 3581 3581 3582 3582 case OP_SUB: 3583 3583 case OP_SUB_NUM: { 3584 - uint8_t fb = func->type_feedback ? func->type_feedback[bc_off] : 0; 3584 + uint8_t fb = sv_func_type_feedback(func) ? sv_func_type_feedback(func)[bc_off] : 0; 3585 3585 bool force_num_only = (op == OP_SUB_NUM); 3586 3586 bool fb_num_only = force_num_only || (fb && !(fb & ~SV_TFB_NUM)); 3587 3587 bool fb_never_num = !force_num_only && fb && !(fb & SV_TFB_NUM); ··· 3754 3754 3755 3755 case OP_MUL: 3756 3756 case OP_MUL_NUM: { 3757 - uint8_t fb = func->type_feedback ? func->type_feedback[bc_off] : 0; 3757 + uint8_t fb = sv_func_type_feedback(func) ? sv_func_type_feedback(func)[bc_off] : 0; 3758 3758 bool force_num_only = (op == OP_MUL_NUM); 3759 3759 bool fb_num_only = force_num_only || (fb && !(fb & ~SV_TFB_NUM)); 3760 3760 bool fb_never_num = !force_num_only && fb && !(fb & SV_TFB_NUM); ··· 3927 3927 3928 3928 case OP_DIV: 3929 3929 case OP_DIV_NUM: { 3930 - uint8_t fb = func->type_feedback ? func->type_feedback[bc_off] : 0; 3930 + uint8_t fb = sv_func_type_feedback(func) ? sv_func_type_feedback(func)[bc_off] : 0; 3931 3931 bool force_num_only = (op == OP_DIV_NUM); 3932 3932 bool fb_num_only = force_num_only || (fb && !(fb & ~SV_TFB_NUM)); 3933 3933 bool fb_never_num = !force_num_only && fb && !(fb & SV_TFB_NUM); ··· 4216 4216 } 4217 4217 4218 4218 case OP_LT: { 4219 - uint8_t fb = func->type_feedback ? func->type_feedback[bc_off] : 0; 4219 + uint8_t fb = sv_func_type_feedback(func) ? sv_func_type_feedback(func)[bc_off] : 0; 4220 4220 bool fb_num_only = fb && !(fb & ~SV_TFB_NUM); 4221 4221 bool fb_never_num = fb && !(fb & SV_TFB_NUM); 4222 4222 ··· 4412 4412 } 4413 4413 4414 4414 case OP_LE: { 4415 - uint8_t fb = func->type_feedback ? func->type_feedback[bc_off] : 0; 4415 + uint8_t fb = sv_func_type_feedback(func) ? sv_func_type_feedback(func)[bc_off] : 0; 4416 4416 bool fb_num_only = fb && !(fb & ~SV_TFB_NUM); 4417 4417 bool fb_never_num = fb && !(fb & SV_TFB_NUM); 4418 4418 ··· 7049 7049 } 7050 7050 7051 7051 case OP_GT: { 7052 - uint8_t fb = func->type_feedback ? func->type_feedback[bc_off] : 0; 7052 + uint8_t fb = sv_func_type_feedback(func) ? sv_func_type_feedback(func)[bc_off] : 0; 7053 7053 bool fb_num_only = fb && !(fb & ~SV_TFB_NUM); 7054 7054 bool fb_never_num = fb && !(fb & SV_TFB_NUM); 7055 7055 ··· 7190 7190 } 7191 7191 7192 7192 case OP_GE: { 7193 - uint8_t fb = func->type_feedback ? func->type_feedback[bc_off] : 0; 7193 + uint8_t fb = sv_func_type_feedback(func) ? sv_func_type_feedback(func)[bc_off] : 0; 7194 7194 bool fb_num_only = fb && !(fb & ~SV_TFB_NUM); 7195 7195 bool fb_never_num = fb && !(fb & SV_TFB_NUM); 7196 7196