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.

type hints

+604 -118
+19
include/silver/engine.h
··· 40 40 uint32_t src_end; 41 41 } sv_srcpos_t; 42 42 43 + typedef enum { 44 + SV_TI_UNKNOWN = 0, 45 + SV_TI_NUM, 46 + SV_TI_STR, 47 + SV_TI_ARR, 48 + SV_TI_OBJ, 49 + SV_TI_BOOL, 50 + SV_TI_NULL, 51 + SV_TI_UNDEF, 52 + } sv_local_type_t; 53 + 54 + typedef struct { 55 + uint8_t type; 56 + } sv_type_info_t; 57 + 43 58 bool sv_lookup_srcpos(sv_func_t *func, int bc_offset, uint32_t *line, uint32_t *col); 44 59 bool sv_lookup_srcspan(sv_func_t *func, int bc_offset, uint32_t *src_off, uint32_t *src_end); 45 60 ··· 56 71 sv_upval_desc_t *upval_descs; 57 72 int max_locals; 58 73 int max_stack; 74 + 75 + sv_type_info_t *local_types; 76 + int local_type_count; 77 + 59 78 int param_count; 60 79 int upvalue_count; 61 80
+5 -1
include/silver/opcode.h
··· 102 102 OP_DEF( SUB, 1, 2, 1, none) 103 103 OP_DEF( MUL, 1, 2, 1, none) 104 104 OP_DEF( DIV, 1, 2, 1, none) 105 + OP_DEF( ADD_NUM, 1, 2, 1, none) /* numeric-only fast path */ 106 + OP_DEF( SUB_NUM, 1, 2, 1, none) /* numeric-only fast path */ 107 + OP_DEF( MUL_NUM, 1, 2, 1, none) /* numeric-only fast path */ 108 + OP_DEF( DIV_NUM, 1, 2, 1, none) /* numeric-only fast path */ 105 109 OP_DEF( MOD, 1, 2, 1, none) 106 110 OP_DEF( EXP, 1, 2, 1, none) 107 111 OP_DEF( NEG, 1, 1, 1, none) /* unary minus */ ··· 177 181 OP_DEF( FOR_IN, 1, 1, 1, none) /* obj -> iterator */ 178 182 OP_DEF( FOR_OF, 1, 1, 3, none) /* iterable -> iter next catch_off */ 179 183 OP_DEF( FOR_AWAIT_OF, 1, 1, 3, none) /* async iterable -> iter next catch_off */ 180 - OP_DEF( ITER_NEXT, 1, 3, 5, none) /* advance iterator */ 184 + OP_DEF( ITER_NEXT, 2, 3, 5, u8) /* advance iterator (u8 hint) */ 181 185 OP_DEF( ITER_GET_VALUE, 1, 2, 3, none) /* catch_off obj -> catch_off value done */ 182 186 OP_DEF( ITER_CLOSE, 1, 3, 0, none) /* close iterator */ 183 187 OP_DEF( ITER_CALL, 2, 4, 5, u8) /* call iterator method */
+310 -12
src/silver/compiler.c
··· 12 12 #include <string.h> 13 13 #include <stdio.h> 14 14 15 + enum { 16 + SV_ITER_HINT_GENERIC = 0, 17 + SV_ITER_HINT_ARRAY = 1, 18 + SV_ITER_HINT_STRING = 4, 19 + }; 20 + 15 21 typedef struct { 16 22 const char *name; 17 23 uint32_t name_len; ··· 19 25 bool is_const; 20 26 bool captured; 21 27 bool is_tdz; 28 + uint8_t inferred_type; 22 29 } sv_local_t; 23 30 24 31 typedef struct { ··· 104 111 uint32_t last_srcpos_off; 105 112 uint32_t last_srcpos_end; 106 113 114 + sv_type_info_t *slot_types; 115 + int slot_type_cap; 116 + 107 117 sv_deferred_export_t *deferred_exports; 108 118 int deferred_export_count; 109 119 int deferred_export_cap; ··· 153 163 static void compile_switch(sv_compiler_t *c, sv_ast_t *node); 154 164 static void compile_label(sv_compiler_t *c, sv_ast_t *node); 155 165 static void compile_class(sv_compiler_t *c, sv_ast_t *node); 166 + 167 + static uint8_t infer_expr_type(sv_compiler_t *c, sv_ast_t *node); 156 168 157 169 static const char *pin_source_text(const char *source, ant_offset_t source_len) { 158 170 if (!source || source_len <= 0) return source; ··· 402 414 c->locals[idx] = (sv_local_t){ 403 415 .name = name, .name_len = len, 404 416 .depth = depth, .is_const = is_const, .captured = false, 417 + .inferred_type = SV_TI_UNKNOWN, 405 418 }; 406 419 return idx; 407 420 } 408 421 422 + static inline bool sv_type_is_known(uint8_t t) { 423 + return t != SV_TI_UNKNOWN; 424 + } 425 + 426 + static inline bool sv_type_is_num(uint8_t t) { 427 + return t == SV_TI_NUM; 428 + } 429 + 430 + static void ensure_slot_type_cap(sv_compiler_t *c, int slot) { 431 + if (slot < 0) return; 432 + if (slot < c->slot_type_cap) return; 433 + int new_cap = c->slot_type_cap ? c->slot_type_cap : 16; 434 + while (new_cap <= slot) new_cap *= 2; 435 + sv_type_info_t *next = realloc(c->slot_types, (size_t)new_cap * sizeof(sv_type_info_t)); 436 + if (!next) return; 437 + memset(next + c->slot_type_cap, 0, (size_t)(new_cap - c->slot_type_cap) * sizeof(sv_type_info_t)); 438 + c->slot_types = next; 439 + c->slot_type_cap = new_cap; 440 + } 441 + 442 + static void mark_slot_type(sv_compiler_t *c, int slot, uint8_t type) { 443 + if (slot < 0) return; 444 + ensure_slot_type_cap(c, slot); 445 + if (!c->slot_types || slot >= c->slot_type_cap) return; 446 + uint8_t old = c->slot_types[slot].type; 447 + if (!sv_type_is_known(type)) { 448 + c->slot_types[slot].type = SV_TI_UNKNOWN; 449 + return; 450 + } 451 + if (!sv_type_is_known(old)) 452 + c->slot_types[slot].type = type; 453 + else if (old != type) 454 + c->slot_types[slot].type = SV_TI_UNKNOWN; 455 + } 456 + 457 + static void set_local_inferred_type(sv_compiler_t *c, int local_idx, uint8_t type) { 458 + if (local_idx < 0 || local_idx >= c->local_count) return; 459 + c->locals[local_idx].inferred_type = type; 460 + if (c->locals[local_idx].depth == -1) return; /* params use arg slots */ 461 + int slot = local_idx - c->param_locals; 462 + mark_slot_type(c, slot, type); 463 + } 464 + 465 + static inline uint8_t get_local_inferred_type(sv_compiler_t *c, int local_idx) { 466 + if (local_idx < 0 || local_idx >= c->local_count) return SV_TI_UNKNOWN; 467 + if (c->locals[local_idx].depth == -1) return SV_TI_UNKNOWN; 468 + if (c->locals[local_idx].is_tdz) return SV_TI_UNKNOWN; 469 + return c->locals[local_idx].inferred_type; 470 + } 471 + 472 + static const char *typeof_name_for_type(uint8_t type) { 473 + switch (type) { 474 + case SV_TI_NUM: return "number"; 475 + case SV_TI_STR: return "string"; 476 + case SV_TI_BOOL: return "boolean"; 477 + case SV_TI_UNDEF: return "undefined"; 478 + case SV_TI_ARR: 479 + case SV_TI_OBJ: 480 + case SV_TI_NULL: return "object"; 481 + default: return NULL; 482 + } 483 + } 484 + 485 + static uint8_t iter_hint_for_type(uint8_t type) { 486 + switch (type) { 487 + case SV_TI_ARR: return SV_ITER_HINT_ARRAY; 488 + case SV_TI_STR: return SV_ITER_HINT_STRING; 489 + default: return SV_ITER_HINT_GENERIC; 490 + } 491 + } 492 + 409 493 static int ensure_local_at_depth( 410 494 sv_compiler_t *c, const char *name, uint32_t len, 411 495 bool is_const, int depth ··· 615 699 616 700 static void emit_get_local(sv_compiler_t *c, int local_idx); 617 701 static void emit_put_local(sv_compiler_t *c, int local_idx); 702 + static void emit_put_local_typed(sv_compiler_t *c, int local_idx, uint8_t type); 618 703 619 704 static void emit_get_var(sv_compiler_t *c, const char *name, uint32_t len) { 620 705 int local = resolve_local(c, name, len); ··· 697 782 emit_const_assign_error(c, name, len); 698 783 return; 699 784 } 785 + set_local_inferred_type(c, local, SV_TI_UNKNOWN); 700 786 701 787 if (c->with_depth > 0) { 702 788 uint8_t kind = c->locals[local].depth == -1 ? WITH_FB_ARG : WITH_FB_LOCAL; ··· 747 833 } 748 834 } 749 835 750 - static void emit_put_local(sv_compiler_t *c, int local_idx) { 836 + static void emit_put_local_typed(sv_compiler_t *c, int local_idx, uint8_t type) { 751 837 int slot = local_idx - c->param_locals; 752 838 if (slot <= 255) { emit_op(c, OP_PUT_LOCAL8); emit(c, (uint8_t)slot); } 753 839 else { emit_op(c, OP_PUT_LOCAL); emit_u16(c, (uint16_t)slot); } 840 + set_local_inferred_type(c, local_idx, type); 841 + } 842 + 843 + static void emit_put_local(sv_compiler_t *c, int local_idx) { 844 + emit_put_local_typed(c, local_idx, SV_TI_UNKNOWN); 754 845 } 755 846 756 847 static void emit_get_local(sv_compiler_t *c, int local_idx) { ··· 915 1006 } 916 1007 for (int j = lb; j < c->local_count; j++) { 917 1008 c->locals[j].is_tdz = true; 1009 + set_local_inferred_type(c, j, SV_TI_UNKNOWN); 918 1010 int slot = j - c->param_locals; 919 1011 emit_op(c, OP_SET_LOCAL_UNDEF); 920 1012 emit_u16(c, (uint16_t)slot); ··· 932 1024 ensure_local_at_depth(c, decl_node->str, decl_node->len, false, c->scope_depth); 933 1025 if (c->local_count > lb) { 934 1026 c->locals[c->local_count - 1].is_tdz = true; 1027 + set_local_inferred_type(c, c->local_count - 1, SV_TI_UNKNOWN); 935 1028 int slot = (c->local_count - 1) - c->param_locals; 936 1029 emit_op(c, OP_SET_LOCAL_UNDEF); 937 1030 emit_u16(c, (uint16_t)slot); ··· 984 1077 } 985 1078 } 986 1079 1080 + static uint8_t infer_expr_type(sv_compiler_t *c, sv_ast_t *node) { 1081 + if (!node) return SV_TI_UNDEF; 1082 + 1083 + switch (node->type) { 1084 + case N_NUMBER: return SV_TI_NUM; 1085 + case N_STRING: return SV_TI_STR; 1086 + case N_BOOL: return SV_TI_BOOL; 1087 + case N_NULL: return SV_TI_NULL; 1088 + case N_UNDEF: return SV_TI_UNDEF; 1089 + case N_ARRAY: return SV_TI_ARR; 1090 + case N_OBJECT: return SV_TI_OBJ; 1091 + case N_TEMPLATE: return SV_TI_STR; 1092 + case N_TYPEOF: return SV_TI_STR; 1093 + case N_VOID: return SV_TI_UNDEF; 1094 + case N_NEW: return SV_TI_OBJ; 1095 + 1096 + case N_IDENT: { 1097 + int local = resolve_local(c, node->str, node->len); 1098 + if (local >= 0) return get_local_inferred_type(c, local); 1099 + return SV_TI_UNKNOWN; 1100 + } 1101 + 1102 + case N_SEQUENCE: 1103 + return infer_expr_type(c, node->right); 1104 + 1105 + case N_TERNARY: { 1106 + uint8_t lt = infer_expr_type(c, node->left); 1107 + uint8_t rhs_type = infer_expr_type(c, node->right); 1108 + if (lt == rhs_type && sv_type_is_known(lt)) return lt; 1109 + return SV_TI_UNKNOWN; 1110 + } 1111 + 1112 + case N_UNARY: { 1113 + uint8_t rhs_type = infer_expr_type(c, node->right); 1114 + switch (node->op) { 1115 + case TOK_UPLUS: 1116 + case TOK_UMINUS: 1117 + return sv_type_is_num(rhs_type) ? SV_TI_NUM : SV_TI_UNKNOWN; 1118 + case TOK_NOT: 1119 + return SV_TI_BOOL; 1120 + default: 1121 + return SV_TI_UNKNOWN; 1122 + } 1123 + } 1124 + 1125 + case N_BINARY: { 1126 + uint8_t lt = infer_expr_type(c, node->left); 1127 + uint8_t rhs_type = infer_expr_type(c, node->right); 1128 + switch (node->op) { 1129 + case TOK_PLUS: 1130 + if (lt == SV_TI_NUM && rhs_type == SV_TI_NUM) return SV_TI_NUM; 1131 + if (lt == SV_TI_STR && rhs_type == SV_TI_STR) return SV_TI_STR; 1132 + return SV_TI_UNKNOWN; 1133 + case TOK_MINUS: 1134 + case TOK_MUL: 1135 + case TOK_DIV: 1136 + return (lt == SV_TI_NUM && rhs_type == SV_TI_NUM) ? SV_TI_NUM : SV_TI_UNKNOWN; 1137 + case TOK_LT: 1138 + case TOK_LE: 1139 + case TOK_GT: 1140 + case TOK_GE: 1141 + case TOK_EQ: 1142 + case TOK_NE: 1143 + case TOK_SEQ: 1144 + case TOK_SNE: 1145 + case TOK_INSTANCEOF: 1146 + case TOK_IN: 1147 + return SV_TI_BOOL; 1148 + case TOK_LAND: 1149 + case TOK_LOR: 1150 + case TOK_NULLISH: 1151 + if (lt == rhs_type && sv_type_is_known(lt)) return lt; 1152 + return SV_TI_UNKNOWN; 1153 + default: 1154 + return SV_TI_UNKNOWN; 1155 + } 1156 + } 1157 + 1158 + default: 1159 + return SV_TI_UNKNOWN; 1160 + } 1161 + } 1162 + 987 1163 static void compile_expr(sv_compiler_t *c, sv_ast_t *node) { 988 1164 if (!node) { emit_op(c, OP_UNDEF); return; } 989 1165 emit_srcpos(c, node); ··· 1256 1432 return; 1257 1433 } 1258 1434 1435 + uint8_t left_type = SV_TI_UNKNOWN; 1436 + uint8_t right_type = SV_TI_UNKNOWN; 1437 + if (op == TOK_PLUS || op == TOK_MINUS || op == TOK_MUL || op == TOK_DIV) { 1438 + left_type = infer_expr_type(c, node->left); 1439 + right_type = infer_expr_type(c, node->right); 1440 + } 1441 + 1259 1442 compile_expr(c, node->left); 1260 1443 compile_expr(c, node->right); 1261 1444 1262 1445 switch (op) { 1263 - case TOK_PLUS: emit_op(c, OP_ADD); break; 1264 - case TOK_MINUS: emit_op(c, OP_SUB); break; 1265 - case TOK_MUL: emit_op(c, OP_MUL); break; 1266 - case TOK_DIV: emit_op(c, OP_DIV); break; 1446 + case TOK_PLUS: 1447 + emit_op(c, (left_type == SV_TI_NUM && right_type == SV_TI_NUM) ? OP_ADD_NUM : OP_ADD); 1448 + break; 1449 + case TOK_MINUS: 1450 + emit_op(c, (left_type == SV_TI_NUM && right_type == SV_TI_NUM) ? OP_SUB_NUM : OP_SUB); 1451 + break; 1452 + case TOK_MUL: 1453 + emit_op(c, (left_type == SV_TI_NUM && right_type == SV_TI_NUM) ? OP_MUL_NUM : OP_MUL); 1454 + break; 1455 + case TOK_DIV: 1456 + emit_op(c, (left_type == SV_TI_NUM && right_type == SV_TI_NUM) ? OP_DIV_NUM : OP_DIV); 1457 + break; 1267 1458 case TOK_REM: emit_op(c, OP_MOD); break; 1268 1459 case TOK_EXP: emit_op(c, OP_EXP); break; 1269 1460 case TOK_LT: emit_op(c, OP_LT); break; ··· 1379 1570 } 1380 1571 1381 1572 if (target->type == N_IDENT) { 1573 + int lhs_local = resolve_local(c, target->str, target->len); 1574 + uint8_t lhs_type = (lhs_local >= 0) ? get_local_inferred_type(c, lhs_local) : SV_TI_UNKNOWN; 1575 + uint8_t rhs_type = infer_expr_type(c, node->right); 1576 + 1382 1577 if (op == TOK_PLUS_ASSIGN) { 1383 1578 int slot = resolve_local_slot(c, target->str, target->len); 1384 1579 if (slot >= 0 && !c->locals[slot + c->param_locals].is_const) { ··· 1393 1588 emit_op(c, OP_ADD_LOCAL); 1394 1589 emit(c, (uint8_t)slot); 1395 1590 emit_get_local(c, c->param_locals + slot); 1591 + set_local_inferred_type(c, c->param_locals + slot, SV_TI_UNKNOWN); 1396 1592 return; 1397 1593 } 1398 1594 } ··· 1414 1610 emit_get_var(c, target->str, target->len); 1415 1611 compile_expr(c, node->right); 1416 1612 switch (op) { 1417 - case TOK_PLUS_ASSIGN: emit_op(c, OP_ADD); break; 1418 - case TOK_MINUS_ASSIGN: emit_op(c, OP_SUB); break; 1419 - case TOK_MUL_ASSIGN: emit_op(c, OP_MUL); break; 1420 - case TOK_DIV_ASSIGN: emit_op(c, OP_DIV); break; 1613 + case TOK_PLUS_ASSIGN: 1614 + emit_op(c, (lhs_type == SV_TI_NUM && rhs_type == SV_TI_NUM) ? OP_ADD_NUM : OP_ADD); 1615 + break; 1616 + case TOK_MINUS_ASSIGN: 1617 + emit_op(c, (lhs_type == SV_TI_NUM && rhs_type == SV_TI_NUM) ? OP_SUB_NUM : OP_SUB); 1618 + break; 1619 + case TOK_MUL_ASSIGN: 1620 + emit_op(c, (lhs_type == SV_TI_NUM && rhs_type == SV_TI_NUM) ? OP_MUL_NUM : OP_MUL); 1621 + break; 1622 + case TOK_DIV_ASSIGN: 1623 + emit_op(c, (lhs_type == SV_TI_NUM && rhs_type == SV_TI_NUM) ? OP_DIV_NUM : OP_DIV); 1624 + break; 1421 1625 case TOK_REM_ASSIGN: emit_op(c, OP_MOD); break; 1422 1626 case TOK_SHL_ASSIGN: emit_op(c, OP_SHL); break; 1423 1627 case TOK_SHR_ASSIGN: emit_op(c, OP_SHR); break; ··· 1561 1765 if (arg->type == N_IDENT) { 1562 1766 int local = resolve_local(c, arg->str, arg->len); 1563 1767 if (local != -1) { 1768 + uint8_t inferred = get_local_inferred_type(c, local); 1769 + const char *known = typeof_name_for_type(inferred); 1770 + if (known) { 1771 + emit_constant(c, js_mkstr(c->js, known, strlen(known))); 1772 + return; 1773 + } 1564 1774 emit_get_var(c, arg->str, arg->len); 1565 1775 } else { 1566 1776 int upval = resolve_upvalue(c, arg->str, arg->len); ··· 2596 2806 } 2597 2807 } else if (kind == SV_VAR_VAR) { 2598 2808 if (decl->right) { 2809 + uint8_t init_type = infer_expr_type(c, decl->right); 2599 2810 compile_expr(c, decl->right); 2600 - compile_lhs_set(c, target, false); 2811 + if (target->type == N_IDENT) { 2812 + int idx = resolve_local(c, target->str, target->len); 2813 + if (idx >= 0 && c->locals[idx].depth != -1) 2814 + emit_put_local_typed(c, idx, init_type); 2815 + else 2816 + compile_lhs_set(c, target, false); 2817 + } else { 2818 + compile_lhs_set(c, target, false); 2819 + } 2601 2820 } 2602 2821 } else { 2603 2822 if (target->type == N_IDENT) { 2604 2823 int idx = ensure_local_at_depth(c, target->str, target->len, 2605 2824 is_const, c->scope_depth); 2825 + uint8_t init_type = SV_TI_UNKNOWN; 2606 2826 if (decl->right) { 2827 + init_type = infer_expr_type(c, decl->right); 2607 2828 compile_expr(c, decl->right); 2608 2829 } else if (!is_const) { 2609 2830 emit_op(c, OP_UNDEF); 2831 + init_type = SV_TI_UNDEF; 2610 2832 } 2611 2833 if (decl->right || !is_const) { 2612 - emit_put_local(c, idx); 2834 + emit_put_local_typed(c, idx, init_type); 2613 2835 c->locals[idx].is_tdz = false; 2614 2836 } 2615 2837 } else { ··· 2628 2850 compile_destructure_pattern(c, pat, false, false, DESTRUCTURE_BIND, kind); 2629 2851 } 2630 2852 2853 + static bool fold_static_typeof_compare( 2854 + sv_compiler_t *c, sv_ast_t *cond, bool *out_truth 2855 + ) { 2856 + if (!cond || cond->type != N_BINARY) return false; 2857 + if (!(cond->op == TOK_SEQ || cond->op == TOK_SNE || 2858 + cond->op == TOK_EQ || cond->op == TOK_NE)) 2859 + return false; 2860 + 2861 + sv_ast_t *typeof_node = NULL; 2862 + sv_ast_t *str_node = NULL; 2863 + if (cond->left && cond->left->type == N_TYPEOF && 2864 + cond->right && cond->right->type == N_STRING) { 2865 + typeof_node = cond->left; 2866 + str_node = cond->right; 2867 + } else if (cond->right && cond->right->type == N_TYPEOF && 2868 + cond->left && cond->left->type == N_STRING) { 2869 + typeof_node = cond->right; 2870 + str_node = cond->left; 2871 + } else return false; 2872 + 2873 + if (!typeof_node->right || typeof_node->right->type != N_IDENT) return false; 2874 + sv_ast_t *ident = typeof_node->right; 2875 + int local = resolve_local(c, ident->str, ident->len); 2876 + if (local < 0) return false; 2877 + const char *known = typeof_name_for_type(get_local_inferred_type(c, local)); 2878 + if (!known) return false; 2879 + 2880 + bool is_equal = (strlen(known) == str_node->len && 2881 + memcmp(known, str_node->str, str_node->len) == 0); 2882 + bool truth = (cond->op == TOK_SEQ || cond->op == TOK_EQ) ? is_equal : !is_equal; 2883 + *out_truth = truth; 2884 + return true; 2885 + } 2886 + 2631 2887 2632 2888 static void compile_if(sv_compiler_t *c, sv_ast_t *node) { 2889 + bool folded_truth = false; 2890 + if (fold_static_typeof_compare(c, node->cond, &folded_truth)) { 2891 + if (folded_truth) compile_stmt(c, node->left); 2892 + else if (node->right) compile_stmt(c, node->right); 2893 + return; 2894 + } 2895 + 2633 2896 compile_expr(c, node->cond); 2634 2897 int else_jump = emit_jump(c, OP_JMP_FALSE); 2635 2898 compile_stmt(c, node->left); ··· 2816 3079 (slot = resolve_local_slot(c, upd->right->str, upd->right->len)) >= 0) { 2817 3080 emit_op(c, upd->op == TOK_POSTINC ? OP_INC_LOCAL : OP_DEC_LOCAL); 2818 3081 emit(c, (uint8_t)slot); 3082 + set_local_inferred_type(c, c->param_locals + slot, SV_TI_UNKNOWN); 2819 3083 } else { 2820 3084 compile_expr(c, upd); 2821 3085 emit_op(c, OP_POP); ··· 2900 3164 } 2901 3165 for (int i = lb; i < c->local_count; i++) { 2902 3166 c->locals[i].is_tdz = true; 3167 + set_local_inferred_type(c, i, SV_TI_UNKNOWN); 2903 3168 int slot = i - c->param_locals; 2904 3169 emit_op(c, OP_SET_LOCAL_UNDEF); 2905 3170 emit_u16(c, (uint16_t)slot); ··· 2933 3198 int iter_inner_start = -1; 2934 3199 2935 3200 bool is_for_await = (node->type == N_FOR_AWAIT_OF); 3201 + uint8_t iter_hint = 0; 3202 + if (is_for_of && !is_for_await) 3203 + iter_hint = iter_hint_for_type(infer_expr_type(c, node->right)); 3204 + 2936 3205 compile_expr(c, node->right); 2937 3206 if (is_for_of) { 2938 3207 emit_op(c, is_for_await ? OP_FOR_AWAIT_OF : OP_FOR_OF); ··· 2953 3222 push_loop(c, loop_start, NULL, 0, false); 2954 3223 2955 3224 if (is_for_of) { 2956 - emit_op(c, is_for_await ? OP_AWAIT_ITER_NEXT : OP_ITER_NEXT); 3225 + if (is_for_await) { 3226 + emit_op(c, OP_AWAIT_ITER_NEXT); 3227 + } else { 3228 + emit_op(c, OP_ITER_NEXT); 3229 + emit(c, iter_hint); 3230 + } 2957 3231 exit_jump = emit_jump(c, OP_JMP_TRUE); 2958 3232 } else { 2959 3233 emit_get_local(c, idx_local); ··· 3447 3721 } 3448 3722 fn->max_locals = comp.max_local_count; 3449 3723 fn->max_stack = fn->max_locals + 64; 3724 + fn->local_type_count = fn->max_locals; 3725 + if (fn->max_locals > 0) { 3726 + fn->local_types = code_arena_bump((size_t)fn->max_locals * sizeof(sv_type_info_t)); 3727 + memset(fn->local_types, 0, (size_t)fn->max_locals * sizeof(sv_type_info_t)); 3728 + if (comp.slot_types) { 3729 + int ncopy = fn->max_locals < comp.slot_type_cap ? fn->max_locals : comp.slot_type_cap; 3730 + memcpy(fn->local_types, comp.slot_types, (size_t)ncopy * sizeof(sv_type_info_t)); 3731 + } 3732 + } 3450 3733 fn->param_count = comp.param_count; 3451 3734 fn->is_strict = comp.is_strict; 3452 3735 fn->filename = c->js->filename; ··· 3459 3742 } 3460 3743 free(comp.code); free(comp.constants); free(comp.atoms); 3461 3744 free(comp.locals); free(comp.upval_descs); free(comp.loops); 3745 + free(comp.slot_types); 3462 3746 3463 3747 int idx = add_constant(c, mkval(T_CFUNC, (uintptr_t)fn)); 3464 3748 emit_op(c, OP_CLOSURE); ··· 3707 3991 if (pname && plen) { 3708 3992 int loc = add_local(&comp, pname, plen, false, 0); 3709 3993 comp.locals[loc].is_tdz = true; 3994 + set_local_inferred_type(&comp, loc, SV_TI_UNKNOWN); 3710 3995 int slot = loc - comp.param_locals; 3711 3996 emit_op(&comp, OP_SET_LOCAL_UNDEF); 3712 3997 emit_u16(&comp, (uint16_t)slot); ··· 3733 4018 comp.locals[bind_lb].is_tdz = false; 3734 4019 if (slot <= 255) { emit_op(&comp, OP_PUT_LOCAL8); emit(&comp, (uint8_t)slot); } 3735 4020 else { emit_op(&comp, OP_PUT_LOCAL); emit_u16(&comp, (uint16_t)slot); } 4021 + set_local_inferred_type(&comp, bind_lb, SV_TI_UNKNOWN); 3736 4022 } 3737 4023 } else if (p->type == N_ASSIGN_PAT) { 3738 4024 emit_op(&comp, OP_GET_ARG); ··· 3748 4034 comp.locals[bind_lb].is_tdz = false; 3749 4035 if (slot <= 255) { emit_op(&comp, OP_PUT_LOCAL8); emit(&comp, (uint8_t)slot); } 3750 4036 else { emit_op(&comp, OP_PUT_LOCAL); emit_u16(&comp, (uint16_t)slot); } 4037 + set_local_inferred_type(&comp, bind_lb, SV_TI_UNKNOWN); 3751 4038 } else if (p->left) { 3752 4039 compile_destructure_binding(&comp, p->left, SV_VAR_LET); 3753 4040 emit_op(&comp, OP_POP); ··· 3770 4057 comp.locals[bind_lb].is_tdz = false; 3771 4058 if (slot <= 255) { emit_op(&comp, OP_PUT_LOCAL8); emit(&comp, (uint8_t)slot); } 3772 4059 else { emit_op(&comp, OP_PUT_LOCAL); emit_u16(&comp, (uint16_t)slot); } 4060 + set_local_inferred_type(&comp, bind_lb, SV_TI_UNKNOWN); 3773 4061 } else if (p->type == N_REST && p->right) { 3774 4062 emit_op(&comp, OP_REST); 3775 4063 emit_u16(&comp, (uint16_t)i); ··· 3896 4184 3897 4185 func->max_locals = max_locals; 3898 4186 func->max_stack = max_locals + 64; 4187 + func->local_type_count = max_locals; 4188 + if (max_locals > 0) { 4189 + func->local_types = code_arena_bump((size_t)max_locals * sizeof(sv_type_info_t)); 4190 + memset(func->local_types, 0, (size_t)max_locals * sizeof(sv_type_info_t)); 4191 + if (comp.slot_types) { 4192 + int ncopy = max_locals < comp.slot_type_cap ? max_locals : comp.slot_type_cap; 4193 + memcpy(func->local_types, comp.slot_types, (size_t)ncopy * sizeof(sv_type_info_t)); 4194 + } 4195 + } 3899 4196 func->param_count = comp.param_count; 3900 4197 func->is_strict = comp.is_strict; 3901 4198 func->is_arrow = comp.is_arrow; ··· 3925 4222 free(comp.upval_descs); 3926 4223 free(comp.loops); 3927 4224 free(comp.srcpos); 4225 + free(comp.slot_types); 3928 4226 3929 4227 return func; 3930 4228 }
+33 -81
src/silver/engine.c
··· 399 399 } 400 400 #endif 401 401 402 - // TODO: generate with X-macro 403 - #define OP(name) [OP_##name] = &&L_##name 404 402 static const void *dispatch[OP__COUNT] = { 405 - OP(INVALID), 406 - 407 - OP(CONST), OP(CONST_I8), OP(CONST8), 408 - OP(UNDEF), OP(NULL), OP(TRUE), OP(FALSE), 409 - OP(THIS), OP(GLOBAL), OP(OBJECT), OP(ARRAY), OP(REGEXP), OP(CLOSURE), 410 - 411 - OP(POP), OP(DUP), OP(DUP2), OP(SWAP), 412 - OP(ROT3L), OP(ROT3R), OP(NIP), OP(NIP2), OP(INSERT2), OP(INSERT3), 413 - OP(SWAP_UNDER), OP(ROT4_UNDER), 414 - 415 - OP(GET_LOCAL), OP(PUT_LOCAL), OP(SET_LOCAL), 416 - OP(GET_LOCAL8), OP(PUT_LOCAL8), OP(SET_LOCAL8), 417 - OP(SET_LOCAL_UNDEF), OP(GET_LOCAL_CHK), OP(PUT_LOCAL_CHK), 418 - 419 - OP(GET_ARG), OP(PUT_ARG), OP(SET_ARG), OP(REST), 420 - OP(GET_UPVAL), OP(PUT_UPVAL), OP(SET_UPVAL), OP(CLOSE_UPVAL), 421 - OP(GET_GLOBAL), OP(GET_GLOBAL_UNDEF), OP(PUT_GLOBAL), 422 - 423 - OP(GET_FIELD), OP(GET_FIELD2), OP(PUT_FIELD), 424 - OP(GET_ELEM), OP(GET_ELEM2), OP(PUT_ELEM), 425 - OP(DEFINE_FIELD), OP(GET_LENGTH), 426 - 427 - OP(GET_FIELD_OPT), OP(GET_ELEM_OPT), 428 - OP(GET_PRIVATE), OP(PUT_PRIVATE), OP(DEF_PRIVATE), 429 - OP(GET_SUPER), OP(GET_SUPER_VAL), OP(PUT_SUPER_VAL), 430 - 431 - OP(ADD), OP(SUB), OP(MUL), OP(DIV), OP(MOD), OP(EXP), 432 - OP(NEG), OP(UPLUS), 433 - OP(INC), OP(DEC), OP(POST_INC), OP(POST_DEC), 434 - OP(INC_LOCAL), OP(DEC_LOCAL), OP(ADD_LOCAL), 435 - 436 - OP(EQ), OP(NE), OP(SEQ), OP(SNE), 437 - OP(LT), OP(LE), OP(GT), OP(GE), 438 - OP(INSTANCEOF), OP(IN), 439 - OP(IS_NULLISH), OP(IS_UNDEF_OR_NULL), 440 - 441 - OP(BAND), OP(BOR), OP(BXOR), OP(BNOT), 442 - OP(SHL), OP(SHR), OP(USHR), 443 - OP(NOT), OP(TYPEOF), OP(VOID), OP(DELETE), OP(DELETE_VAR), 444 - 445 - OP(JMP), OP(JMP_FALSE), OP(JMP_TRUE), 446 - OP(JMP_FALSE_PEEK), OP(JMP_TRUE_PEEK), OP(JMP_NOT_NULLISH), 447 - OP(JMP8), OP(JMP_FALSE8), OP(JMP_TRUE8), 448 - 449 - OP(CALL), OP(CALL_METHOD), 450 - OP(TAIL_CALL), OP(TAIL_CALL_METHOD), 451 - OP(NEW), OP(APPLY), OP(EVAL), 452 - OP(RETURN), OP(RETURN_UNDEF), OP(RETURN_ASYNC), 453 - OP(CHECK_CTOR), OP(CHECK_CTOR_RET), OP(HALT), 454 - 455 - OP(THROW), OP(THROW_ERROR), 456 - OP(TRY_PUSH), OP(TRY_POP), 457 - OP(CATCH), OP(FINALLY), OP(FINALLY_RET), OP(NIP_CATCH), 458 - 459 - OP(FOR_IN), OP(FOR_OF), OP(FOR_AWAIT_OF), 460 - OP(ITER_NEXT), OP(ITER_GET_VALUE), 461 - OP(ITER_CLOSE), OP(ITER_CALL), OP(AWAIT_ITER_NEXT), 462 - 463 - OP(AWAIT), OP(YIELD), OP(YIELD_STAR), OP(INITIAL_YIELD), 464 - OP(SPREAD), 465 - 466 - OP(DEFINE_METHOD), OP(DEFINE_METHOD_COMP), 467 - OP(SET_NAME), OP(SET_NAME_COMP), 468 - OP(SET_PROTO), OP(SET_HOME_OBJ), 469 - OP(APPEND), OP(COPY_DATA_PROPS), 470 - 471 - OP(DEFINE_CLASS), OP(DEFINE_CLASS_COMP), OP(ADD_BRAND), 472 - OP(TO_OBJECT), OP(TO_PROPKEY), OP(IS_UNDEF), OP(IS_NULL), 473 - 474 - OP(IMPORT), OP(IMPORT_SYNC), OP(IMPORT_DEFAULT), OP(EXPORT), OP(EXPORT_ALL), 475 - OP(ENTER_WITH), OP(EXIT_WITH), 476 - 477 - OP(WITH_GET_VAR), OP(WITH_PUT_VAR), OP(WITH_DEL_VAR), 478 - OP(SPECIAL_OBJ), OP(EMPTY), OP(DEBUGGER), OP(NOP), OP(PUT_CONST), 479 - OP(LABEL), OP(LINE_NUM), OP(COL_NUM), 403 + #define OP_DEF(name, size, n_pop, n_push, f) [OP_##name] = &&L_##name, 404 + #include "silver/opcode.h" 480 405 }; 481 - #undef OP 482 406 483 407 ant_value_t sv_err; 484 408 #define VM_CHECK(expr) do { \ ··· 492 416 #define NEXT(n) do { ip += (n); DISPATCH(); } while (0) 493 417 494 418 #ifdef ANT_JIT 495 - #define JIT_OSR_BACK_EDGE() do { \ 419 + #define JIT_OSR_BACK_EDGE() do { \ 496 420 if (!func->jit_compile_failed) { \ 497 421 if (!func->type_feedback) sv_tfb_ensure(func); \ 498 422 if (++func->back_edge_count >= SV_JIT_OSR_THRESHOLD) { \ 499 - ant_value_t osr_r = sv_jit_try_osr( \ 423 + ant_value_t osr_r = sv_jit_try_osr( \ 500 424 vm, js, frame, func, \ 501 425 (int)(ip - func->code)); \ 502 426 if (osr_r != SV_JIT_RETRY_INTERP) { \ ··· 605 529 } 606 530 VM_CHECK(sv_op_add(vm, js)); NEXT(1); 607 531 } 532 + 533 + L_ADD_NUM: { 534 + ant_value_t r = vm->stack[--vm->sp]; 535 + ant_value_t l = vm->stack[vm->sp - 1]; 536 + vm->stack[vm->sp - 1] = tov(tod(l) + tod(r)); 537 + NEXT(1); 538 + } 608 539 609 540 L_SUB: { 610 541 ant_value_t r = vm->stack[vm->sp - 1], l = vm->stack[vm->sp - 2]; ··· 613 544 vm->sp--; vm->stack[vm->sp - 1] = tov(tod(l) - tod(r)); NEXT(1); 614 545 } 615 546 VM_CHECK(sv_op_sub(vm, js)); NEXT(1); 547 + } 548 + 549 + L_SUB_NUM: { 550 + ant_value_t r = vm->stack[--vm->sp]; 551 + ant_value_t l = vm->stack[vm->sp - 1]; 552 + vm->stack[vm->sp - 1] = tov(tod(l) - tod(r)); 553 + NEXT(1); 554 + } 555 + 556 + L_MUL_NUM: { 557 + ant_value_t r = vm->stack[--vm->sp]; 558 + ant_value_t l = vm->stack[vm->sp - 1]; 559 + vm->stack[vm->sp - 1] = tov(tod(l) * tod(r)); 560 + NEXT(1); 561 + } 562 + 563 + L_DIV_NUM: { 564 + ant_value_t r = vm->stack[--vm->sp]; 565 + ant_value_t l = vm->stack[vm->sp - 1]; 566 + vm->stack[vm->sp - 1] = tov(tod(l) / tod(r)); 567 + NEXT(1); 616 568 } 617 569 618 570 L_MUL: { sv_tfb_record2(func, ip, vm->stack[vm->sp-2], vm->stack[vm->sp-1]); VM_CHECK(sv_op_mul(vm, js)); NEXT(1); } ··· 1243 1195 L_FOR_IN: { VM_CHECK(sv_op_for_in(vm, js)); NEXT(1); } 1244 1196 L_FOR_OF: { VM_CHECK(sv_op_for_of(vm, js)); NEXT(1); } 1245 1197 L_FOR_AWAIT_OF: { VM_CHECK(sv_op_for_await_of(vm, js)); NEXT(1); } 1246 - L_ITER_NEXT: { VM_CHECK(sv_op_iter_next(vm, js)); NEXT(1); } 1198 + L_ITER_NEXT: { VM_CHECK(sv_op_iter_next(vm, js, ip)); NEXT(2); } 1247 1199 L_ITER_GET_VALUE: { sv_op_iter_get_value(vm, js); NEXT(1); } 1248 1200 L_ITER_CLOSE: { sv_op_iter_close(vm, js); NEXT(1); } 1249 1201 L_ITER_CALL: { VM_CHECK(sv_op_iter_call(vm, js, ip)); NEXT(2); }
+3 -2
src/silver/ops/iteration.h
··· 134 134 return tov(0); 135 135 } 136 136 137 - static inline ant_value_t sv_op_iter_next(sv_vm_t *vm, ant_t *js) { 138 - int tag = (int)js_getnum(vm->stack[vm->sp - 1]); 137 + static inline ant_value_t sv_op_iter_next(sv_vm_t *vm, ant_t *js, uint8_t *ip) { 138 + int hint = (int)sv_get_u8(ip + 1); 139 + int tag = hint ? hint : (int)js_getnum(vm->stack[vm->sp - 1]); 139 140 switch (tag) { 140 141 case SV_ITER_ARRAY: { 141 142 ant_value_t arr = vm->stack[vm->sp - 3];
+72 -22
src/silver/swarm.c
··· 403 403 case OP_PUT_LOCAL: case OP_PUT_LOCAL8: 404 404 case OP_SET_LOCAL: case OP_SET_LOCAL8: 405 405 case OP_POP: case OP_DUP: 406 - case OP_ADD: case OP_SUB: case OP_MUL: case OP_DIV: case OP_MOD: 406 + case OP_ADD: case OP_SUB: case OP_MUL: case OP_DIV: 407 + case OP_ADD_NUM: case OP_SUB_NUM: case OP_MUL_NUM: case OP_DIV_NUM: 408 + case OP_MOD: 407 409 case OP_LT: case OP_LE: 408 410 case OP_RETURN: case OP_RETURN_UNDEF: 409 411 case OP_NOP: case OP_LINE_NUM: case OP_COL_NUM: case OP_LABEL: ··· 560 562 break; 561 563 } 562 564 563 - case OP_ADD: case OP_SUB: case OP_MUL: case OP_DIV: { 565 + case OP_ADD: case OP_SUB: case OP_MUL: case OP_DIV: 566 + case OP_ADD_NUM: case OP_SUB_NUM: case OP_MUL_NUM: case OP_DIV_NUM: { 564 567 MIR_reg_t rr = inl_vs[--isp]; 565 568 MIR_reg_t rl = inl_vs[--isp]; 566 569 MIR_reg_t rd = inl_vs[isp++]; 567 570 568 - mir_emit_is_num_guard(ctx, jit_func, r_bool, rl, slow); 569 - mir_emit_is_num_guard(ctx, jit_func, r_bool, rr, slow); 571 + if (!(op == OP_ADD_NUM || op == OP_SUB_NUM || 572 + op == OP_MUL_NUM || op == OP_DIV_NUM)) { 573 + mir_emit_is_num_guard(ctx, jit_func, r_bool, rl, slow); 574 + mir_emit_is_num_guard(ctx, jit_func, r_bool, rr, slow); 575 + } 570 576 571 577 if (!*p_d_slot) { 572 578 *p_d_slot = MIR_new_func_reg(ctx, jit_func->u.func, ··· 591 597 592 598 MIR_insn_code_t mir_op; 593 599 switch (op) { 594 - case OP_ADD: mir_op = MIR_DADD; break; 595 - case OP_SUB: mir_op = MIR_DSUB; break; 596 - case OP_MUL: mir_op = MIR_DMUL; break; 597 - default: mir_op = MIR_DDIV; break; 600 + case OP_ADD: 601 + case OP_ADD_NUM: mir_op = MIR_DADD; break; 602 + case OP_SUB: 603 + case OP_SUB_NUM: mir_op = MIR_DSUB; break; 604 + case OP_MUL: 605 + case OP_MUL_NUM: mir_op = MIR_DMUL; break; 606 + default: mir_op = MIR_DDIV; break; 598 607 } 599 608 MIR_append_insn(ctx, jit_func, 600 609 MIR_new_insn(ctx, mir_op, ··· 730 739 if (sz == 0) break; 731 740 switch (op) { 732 741 case OP_ADD: case OP_SUB: case OP_MUL: case OP_DIV: case OP_MOD: 742 + case OP_ADD_NUM: case OP_SUB_NUM: case OP_MUL_NUM: case OP_DIV_NUM: 733 743 case OP_NEG: 734 744 case OP_LT: case OP_LE: case OP_GT: case OP_GE: 735 745 case OP_BAND: case OP_BOR: case OP_BXOR: case OP_BNOT: ··· 784 794 case OP_POP: case OP_DUP: case OP_DUP2: 785 795 case OP_INSERT2: case OP_INSERT3: 786 796 case OP_ADD: case OP_SUB: case OP_MUL: case OP_DIV: case OP_MOD: 797 + case OP_ADD_NUM: case OP_SUB_NUM: case OP_MUL_NUM: case OP_DIV_NUM: 787 798 case OP_NEG: case OP_IS_UNDEF: case OP_IS_NULL: 788 799 case OP_LT: case OP_LE: case OP_GT: case OP_GE: 789 800 case OP_NE: case OP_SNE: ··· 1180 1191 int n_locals = func->max_locals; 1181 1192 MIR_reg_t *local_regs = NULL; 1182 1193 sv_func_t **known_func_locals = NULL; 1194 + uint8_t *known_type_locals = NULL; 1183 1195 if (n_locals > 0) { 1184 1196 local_regs = calloc((size_t)n_locals, sizeof(MIR_reg_t)); 1185 1197 known_func_locals = calloc((size_t)n_locals, sizeof(sv_func_t *)); 1186 - if (!local_regs) { free(vs.regs); free(vs.known_func); free(vs.d_regs); free(vs.slot_type); free(known_func_locals); MIR_finish_func(ctx); MIR_finish_module(ctx); return NULL; } 1198 + known_type_locals = calloc((size_t)n_locals, sizeof(uint8_t)); 1199 + if (!local_regs || !known_func_locals || !known_type_locals) { 1200 + free(vs.regs); free(vs.known_func); free(vs.d_regs); free(vs.slot_type); 1201 + free(local_regs); free(known_func_locals); free(known_type_locals); 1202 + MIR_finish_func(ctx); MIR_finish_module(ctx); return NULL; 1203 + } 1204 + if (func->local_types && func->local_type_count > 0) { 1205 + int ncopy = func->local_type_count < n_locals ? func->local_type_count : n_locals; 1206 + for (int i = 0; i < ncopy; i++) 1207 + known_type_locals[i] = func->local_types[i].type; 1208 + } 1187 1209 for (int i = 0; i < n_locals; i++) { 1188 1210 char rname[32]; 1189 1211 snprintf(rname, sizeof(rname), "l%d", i); ··· 1548 1570 MIR_new_insn(ctx, MIR_MOV, 1549 1571 MIR_new_reg_op(ctx, dst), 1550 1572 MIR_new_reg_op(ctx, local_regs[idx]))); 1573 + if (known_type_locals && known_type_locals[idx] == SV_TI_NUM) { 1574 + mir_i64_to_d(ctx, jit_func, vs.d_regs[vs.sp - 1], dst, r_d_slot); 1575 + if (vs.slot_type) vs.slot_type[vs.sp - 1] = SLOT_NUM; 1576 + } 1551 1577 break; 1552 1578 } 1553 1579 case OP_GET_LOCAL8: { ··· 1565 1591 MIR_new_insn(ctx, MIR_MOV, 1566 1592 MIR_new_reg_op(ctx, dst), 1567 1593 MIR_new_reg_op(ctx, local_regs[idx]))); 1594 + if (known_type_locals && known_type_locals[idx] == SV_TI_NUM) { 1595 + mir_i64_to_d(ctx, jit_func, vs.d_regs[vs.sp - 1], dst, r_d_slot); 1596 + if (vs.slot_type) vs.slot_type[vs.sp - 1] = SLOT_NUM; 1597 + } 1568 1598 break; 1569 1599 } 1570 1600 ··· 1575 1605 vstack_ensure_boxed(&vs, vs.sp - 1, ctx, jit_func, r_d_slot); 1576 1606 MIR_reg_t src = vstack_pop(&vs); 1577 1607 if (known_func_locals) known_func_locals[idx] = kf; 1608 + if (known_type_locals) known_type_locals[idx] = SV_TI_UNKNOWN; 1578 1609 MIR_append_insn(ctx, jit_func, 1579 1610 MIR_new_insn(ctx, MIR_MOV, 1580 1611 MIR_new_reg_op(ctx, local_regs[idx]), ··· 1594 1625 vstack_ensure_boxed(&vs, vs.sp - 1, ctx, jit_func, r_d_slot); 1595 1626 MIR_reg_t src = vstack_pop(&vs); 1596 1627 if (known_func_locals) known_func_locals[idx] = kf; 1628 + if (known_type_locals) known_type_locals[idx] = SV_TI_UNKNOWN; 1597 1629 MIR_append_insn(ctx, jit_func, 1598 1630 MIR_new_insn(ctx, MIR_MOV, 1599 1631 MIR_new_reg_op(ctx, local_regs[idx]), ··· 1613 1645 vstack_ensure_boxed(&vs, vs.sp - 1, ctx, jit_func, r_d_slot); 1614 1646 MIR_reg_t src = vstack_top(&vs); 1615 1647 if (known_func_locals) known_func_locals[idx] = vs.known_func[vs.sp - 1]; 1648 + if (known_type_locals) known_type_locals[idx] = SV_TI_UNKNOWN; 1616 1649 MIR_append_insn(ctx, jit_func, 1617 1650 MIR_new_insn(ctx, MIR_MOV, 1618 1651 MIR_new_reg_op(ctx, local_regs[idx]), ··· 1631 1664 vstack_ensure_boxed(&vs, vs.sp - 1, ctx, jit_func, r_d_slot); 1632 1665 MIR_reg_t src = vstack_top(&vs); 1633 1666 if (known_func_locals) known_func_locals[idx] = vs.known_func[vs.sp - 1]; 1667 + if (known_type_locals) known_type_locals[idx] = SV_TI_UNKNOWN; 1634 1668 MIR_append_insn(ctx, jit_func, 1635 1669 MIR_new_insn(ctx, MIR_MOV, 1636 1670 MIR_new_reg_op(ctx, local_regs[idx]), ··· 1644 1678 break; 1645 1679 } 1646 1680 1647 - case OP_SET_LOCAL_UNDEF: 1681 + case OP_SET_LOCAL_UNDEF: { 1682 + uint16_t idx = sv_get_u16(ip + 1); 1683 + if (idx >= (uint16_t)n_locals) { ok = false; break; } 1684 + if (known_type_locals) known_type_locals[idx] = SV_TI_UNKNOWN; 1648 1685 break; 1686 + } 1649 1687 1650 1688 case OP_POP: 1651 1689 vstack_pop(&vs); ··· 1741 1779 break; 1742 1780 } 1743 1781 1744 - case OP_ADD: { 1782 + case OP_ADD: 1783 + case OP_ADD_NUM: { 1745 1784 uint8_t fb = func->type_feedback ? func->type_feedback[bc_off] : 0; 1746 - bool fb_num_only = fb && !(fb & ~SV_TFB_NUM); 1747 - bool fb_never_num = fb && !(fb & SV_TFB_NUM); 1785 + bool force_num_only = (op == OP_ADD_NUM); 1786 + bool fb_num_only = force_num_only || (fb && !(fb & ~SV_TFB_NUM)); 1787 + bool fb_never_num = !force_num_only && fb && !(fb & SV_TFB_NUM); 1748 1788 1749 1789 bool l_is_num = vs.slot_type && vs.slot_type[vs.sp - 2] == SLOT_NUM; 1750 1790 bool r_is_num = vs.slot_type && vs.slot_type[vs.sp - 1] == SLOT_NUM; ··· 1929 1969 break; 1930 1970 } 1931 1971 1932 - case OP_SUB: { 1972 + case OP_SUB: 1973 + case OP_SUB_NUM: { 1933 1974 uint8_t fb = func->type_feedback ? func->type_feedback[bc_off] : 0; 1934 - bool fb_num_only = fb && !(fb & ~SV_TFB_NUM); 1935 - bool fb_never_num = fb && !(fb & SV_TFB_NUM); 1975 + bool force_num_only = (op == OP_SUB_NUM); 1976 + bool fb_num_only = force_num_only || (fb && !(fb & ~SV_TFB_NUM)); 1977 + bool fb_never_num = !force_num_only && fb && !(fb & SV_TFB_NUM); 1936 1978 1937 1979 bool l_is_num = vs.slot_type && vs.slot_type[vs.sp - 2] == SLOT_NUM; 1938 1980 bool r_is_num = vs.slot_type && vs.slot_type[vs.sp - 1] == SLOT_NUM; ··· 2100 2142 break; 2101 2143 } 2102 2144 2103 - case OP_MUL: { 2145 + case OP_MUL: 2146 + case OP_MUL_NUM: { 2104 2147 uint8_t fb = func->type_feedback ? func->type_feedback[bc_off] : 0; 2105 - bool fb_num_only = fb && !(fb & ~SV_TFB_NUM); 2106 - bool fb_never_num = fb && !(fb & SV_TFB_NUM); 2148 + bool force_num_only = (op == OP_MUL_NUM); 2149 + bool fb_num_only = force_num_only || (fb && !(fb & ~SV_TFB_NUM)); 2150 + bool fb_never_num = !force_num_only && fb && !(fb & SV_TFB_NUM); 2107 2151 2108 2152 bool l_is_num = vs.slot_type && vs.slot_type[vs.sp - 2] == SLOT_NUM; 2109 2153 bool r_is_num = vs.slot_type && vs.slot_type[vs.sp - 1] == SLOT_NUM; ··· 2271 2315 break; 2272 2316 } 2273 2317 2274 - case OP_DIV: { 2318 + case OP_DIV: 2319 + case OP_DIV_NUM: { 2275 2320 uint8_t fb = func->type_feedback ? func->type_feedback[bc_off] : 0; 2276 - bool fb_num_only = fb && !(fb & ~SV_TFB_NUM); 2277 - bool fb_never_num = fb && !(fb & SV_TFB_NUM); 2321 + bool force_num_only = (op == OP_DIV_NUM); 2322 + bool fb_num_only = force_num_only || (fb && !(fb & ~SV_TFB_NUM)); 2323 + bool fb_never_num = !force_num_only && fb && !(fb & SV_TFB_NUM); 2278 2324 2279 2325 bool l_is_num = vs.slot_type && vs.slot_type[vs.sp - 2] == SLOT_NUM; 2280 2326 bool r_is_num = vs.slot_type && vs.slot_type[vs.sp - 1] == SLOT_NUM; ··· 3621 3667 uint8_t idx = sv_get_u8(ip + 1); 3622 3668 if (idx >= (uint8_t)n_locals) { ok = false; break; } 3623 3669 if (known_func_locals) known_func_locals[idx] = NULL; 3670 + if (known_type_locals) known_type_locals[idx] = SV_TI_UNKNOWN; 3624 3671 int in = arith_n++; 3625 3672 char il_d1[32], il_d2[32]; 3626 3673 snprintf(il_d1, sizeof(il_d1), "il_d1_%d", in); ··· 3647 3694 uint8_t idx = sv_get_u8(ip + 1); 3648 3695 if (idx >= (uint8_t)n_locals) { ok = false; break; } 3649 3696 if (known_func_locals) known_func_locals[idx] = NULL; 3697 + if (known_type_locals) known_type_locals[idx] = SV_TI_UNKNOWN; 3650 3698 int dn = arith_n++; 3651 3699 char dl_d1[32], dl_d2[32]; 3652 3700 snprintf(dl_d1, sizeof(dl_d1), "dl_d1_%d", dn); ··· 3673 3721 uint8_t idx = sv_get_u8(ip + 1); 3674 3722 if (idx >= (uint8_t)n_locals) { ok = false; break; } 3675 3723 if (known_func_locals) known_func_locals[idx] = NULL; 3724 + if (known_type_locals) known_type_locals[idx] = SV_TI_UNKNOWN; 3676 3725 MIR_reg_t rr = vstack_pop(&vs); 3677 3726 3678 3727 MIR_label_t slow = MIR_new_label(ctx); ··· 5217 5266 free(vs.slot_type); 5218 5267 free(local_regs); 5219 5268 free(known_func_locals); 5269 + free(known_type_locals); 5220 5270 free(captured_locals); 5221 5271 5222 5272 if (!ok) return NULL;
+162
tests/bench_type_tracking.js
··· 1 + const now = () => (typeof performance !== 'undefined' && performance.now ? performance.now() : Date.now()); 2 + 3 + function readScale() { 4 + if (typeof process === 'undefined' || !process || !process.argv) return 1; 5 + const raw = Number(process.argv[2]); 6 + return Number.isFinite(raw) && raw > 0 ? raw : 1; 7 + } 8 + 9 + const SCALE = readScale(); 10 + const REPEATS = 5; 11 + 12 + function opaque(v) { 13 + return v; 14 + } 15 + 16 + function runCase(name, rounds, fn) { 17 + // Warm up once with a smaller trip count. 18 + fn(Math.max(1, (rounds / 8) | 0)); 19 + 20 + const samples = []; 21 + let out = 0; 22 + for (let i = 0; i < REPEATS; i++) { 23 + const t0 = now(); 24 + out = fn(rounds); 25 + samples.push(now() - t0); 26 + } 27 + 28 + let best = samples[0]; 29 + let sum = 0; 30 + for (let i = 0; i < samples.length; i++) { 31 + if (samples[i] < best) best = samples[i]; 32 + sum += samples[i]; 33 + } 34 + const avg = sum / samples.length; 35 + console.log(`${name}: best ${best.toFixed(2)} ms, avg ${avg.toFixed(2)} ms`); 36 + return { best, avg, out }; 37 + } 38 + 39 + function reportPair(title, rounds, typedFn, unknownFn) { 40 + console.log(`\n== ${title} (${rounds} rounds) ==`); 41 + const typed = runCase('typed-friendly', rounds, typedFn); 42 + const unknown = runCase('type-unknown ', rounds, unknownFn); 43 + 44 + if (typed.out !== unknown.out) { 45 + throw new Error(`${title}: result mismatch (${typed.out} vs ${unknown.out})`); 46 + } 47 + 48 + const ratio = unknown.best / typed.best; 49 + const delta = unknown.best - typed.best; 50 + const winner = ratio >= 1 ? 'typed-friendly' : 'type-unknown'; 51 + console.log(`speedup: ${ratio.toFixed(2)}x (winner: ${winner}, delta ${delta.toFixed(2)} ms)`); 52 + return typed.out; 53 + } 54 + 55 + function forOfArrayTyped(rounds) { 56 + const arr = [ 57 + 1, 3, 5, 7, 9, 11, 13, 15, 58 + 17, 19, 21, 23, 25, 27, 29, 31, 59 + 33, 35, 37, 39, 41, 43, 45, 47, 60 + 49, 51, 53, 55, 57, 59, 61, 63 61 + ]; 62 + let sum = 0; 63 + for (let r = 0; r < rounds; r++) { 64 + for (const v of arr) sum += v; 65 + } 66 + return sum; 67 + } 68 + 69 + function forOfArrayUnknown(rounds) { 70 + const arr = [ 71 + 1, 3, 5, 7, 9, 11, 13, 15, 72 + 17, 19, 21, 23, 25, 27, 29, 31, 73 + 33, 35, 37, 39, 41, 43, 45, 47, 74 + 49, 51, 53, 55, 57, 59, 61, 63 75 + ]; 76 + const iterSrc = opaque(arr); 77 + let sum = 0; 78 + for (let r = 0; r < rounds; r++) { 79 + for (const v of iterSrc) sum += v; 80 + } 81 + return sum; 82 + } 83 + 84 + function forOfStringTyped(rounds) { 85 + const str = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; 86 + let sum = 0; 87 + for (let r = 0; r < rounds; r++) { 88 + for (const ch of str) sum += ch.length; 89 + } 90 + return sum; 91 + } 92 + 93 + function forOfStringUnknown(rounds) { 94 + const str = opaque('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'); 95 + let sum = 0; 96 + for (let r = 0; r < rounds; r++) { 97 + for (const ch of str) sum += ch.length; 98 + } 99 + return sum; 100 + } 101 + 102 + function arithmeticTyped(rounds) { 103 + const a = 1.25; 104 + const b = 3.75; 105 + const c = 0.5; 106 + const d = 9.125; 107 + let acc = 0; 108 + for (let i = 0; i < rounds; i++) { 109 + acc = acc + (a * b) - (c / d) + (a + c) - (b - d); 110 + } 111 + return acc; 112 + } 113 + 114 + function arithmeticUnknown(rounds) { 115 + const a = opaque(1.25); 116 + const b = opaque(3.75); 117 + const c = opaque(0.5); 118 + const d = opaque(9.125); 119 + let acc = 0; 120 + for (let i = 0; i < rounds; i++) { 121 + acc = acc + (a * b) - (c / d) + (a + c) - (b - d); 122 + } 123 + return acc; 124 + } 125 + 126 + function typeofGuardTyped(rounds) { 127 + const x = 123; 128 + let sum = 0; 129 + for (let i = 0; i < rounds; i++) { 130 + if (typeof x === 'number') sum += x; 131 + else sum -= 1; 132 + } 133 + return sum; 134 + } 135 + 136 + function typeofGuardUnknown(rounds) { 137 + const x = opaque(123); 138 + let sum = 0; 139 + for (let i = 0; i < rounds; i++) { 140 + if (typeof x === 'number') sum += x; 141 + else sum -= 1; 142 + } 143 + return sum; 144 + } 145 + 146 + const roundsForOfArray = Math.max(20000, Math.floor(70000 * SCALE)); 147 + const roundsForOfString = Math.max(15000, Math.floor(50000 * SCALE)); 148 + const roundsArithmetic = Math.max(500000, Math.floor(2500000 * SCALE)); 149 + const roundsTypeof = Math.max(500000, Math.floor(3000000 * SCALE)); 150 + 151 + console.log('compile-time type tracking benchmark'); 152 + console.log(`scale: ${SCALE}`); 153 + console.log(`repeats: ${REPEATS}`); 154 + console.log('usage: ./build/ant tests/bench_type_tracking.js [scale]'); 155 + 156 + let checksum = 0; 157 + checksum += reportPair('for..of array iterator hint', roundsForOfArray, forOfArrayTyped, forOfArrayUnknown); 158 + checksum += reportPair('for..of string iterator hint', roundsForOfString, forOfStringTyped, forOfStringUnknown); 159 + checksum += reportPair('numeric arithmetic specialization', roundsArithmetic, arithmeticTyped, arithmeticUnknown); 160 + checksum += reportPair('typeof guard folding', roundsTypeof, typeofGuardTyped, typeofGuardUnknown); 161 + 162 + console.log(`\nchecksum: ${checksum}`);