jz 0.7.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/README.md +37 -33
  2. package/bench/README.md +176 -73
  3. package/bench/bench.svg +58 -71
  4. package/cli.js +12 -5
  5. package/dist/interop.js +1 -1
  6. package/dist/jz.js +1366 -1101
  7. package/index.js +40 -9
  8. package/interop.js +193 -138
  9. package/layout.js +29 -18
  10. package/module/array.js +49 -73
  11. package/module/collection.js +83 -25
  12. package/module/console.js +1 -1
  13. package/module/core.js +161 -15
  14. package/module/json.js +3 -3
  15. package/module/math.js +167 -117
  16. package/module/number.js +247 -13
  17. package/module/object.js +11 -5
  18. package/module/regex.js +8 -7
  19. package/module/string.js +295 -171
  20. package/module/typedarray.js +169 -105
  21. package/package.json +7 -3
  22. package/src/abi/string.js +40 -35
  23. package/src/ast.js +19 -2
  24. package/src/compile/analyze.js +64 -2
  25. package/src/compile/cse-load.js +200 -0
  26. package/src/compile/emit-assign.js +73 -14
  27. package/src/compile/emit.js +324 -34
  28. package/src/compile/index.js +204 -61
  29. package/src/compile/infer.js +8 -1
  30. package/src/compile/loop-divmod.js +12 -58
  31. package/src/compile/loop-model.js +91 -0
  32. package/src/compile/loop-recurrence.js +167 -0
  33. package/src/compile/loop-square.js +102 -0
  34. package/src/compile/narrow.js +180 -34
  35. package/src/compile/peel-stencil.js +18 -64
  36. package/src/compile/plan/common.js +29 -0
  37. package/src/compile/plan/index.js +4 -1
  38. package/src/compile/plan/inline.js +176 -21
  39. package/src/compile/plan/literals.js +93 -19
  40. package/src/ctx.js +51 -12
  41. package/src/helper-counters.js +137 -0
  42. package/src/ir.js +102 -13
  43. package/src/kind-traits.js +7 -3
  44. package/src/kind.js +14 -1
  45. package/src/op-policy.js +5 -2
  46. package/src/ops.js +119 -0
  47. package/src/optimize/index.js +1125 -136
  48. package/src/optimize/recurse.js +182 -0
  49. package/src/optimize/vectorize.js +1302 -144
  50. package/src/prepare/index.js +29 -12
  51. package/src/reps.js +4 -1
  52. package/src/type.js +53 -45
  53. package/src/wat/assemble.js +92 -9
  54. package/src/widen.js +21 -0
  55. package/src/wat/optimize.js +0 -3938
@@ -5,9 +5,13 @@
5
5
 
6
6
  import { VAL } from './reps.js'
7
7
 
8
- export const BOOL_OPS = new Set([
9
- '!', '<', '<=', '>', '>=', '==', '!=', '===', '!==', 'in', 'instanceof',
10
- ])
8
+ // Comparison / logical-not ops — result is a 0|1 boolean carried as i32. The one
9
+ // source of truth for "this operator yields a boolean": valTypeOf reads it as
10
+ // VAL.BOOL, exprType/isIntExpr read it as integer-certain. `in`/`instanceof` also
11
+ // yield a boolean but are membership ops, kept out of the integer-certainty set
12
+ // (they throw on BigInt operands and never reach numeric analysis).
13
+ export const CMP_OPS = new Set(['!', '<', '<=', '>', '>=', '==', '!=', '===', '!=='])
14
+ export const BOOL_OPS = new Set([...CMP_OPS, 'in', 'instanceof'])
11
15
 
12
16
  export const NUMERIC_BINARY_OPS = ['-', 'u-', '*', '/', '%', '&', '|', '^', '<<', '>>']
13
17
  export const NUMERIC_UNARY_OPS = new Set(['**', '++', '--', '~', '>>>', 'u+'])
package/src/kind.js CHANGED
@@ -137,7 +137,16 @@ VT['?:'] = (args) => {
137
137
  const truthy = literalTruthiness(args[0])
138
138
  if (truthy != null) return valTypeOf(truthy ? args[1] : args[2])
139
139
  const ta = valTypeOf(args[1]), tb = valTypeOf(args[2])
140
- return ta && ta === tb ? ta : null
140
+ if (ta && ta === tb) return ta
141
+ // A boolean branch coerces to 0/1 in numeric context (same rule as &&/||/?? below):
142
+ // when the other branch has a known non-boolean type, the conditional carries it.
143
+ // Without this, `num + (cond ? num : num>k)` sees a null-typed operand and emits the
144
+ // polymorphic string-concat dispatch on two pure-numeric subexprs — which pins the
145
+ // whole number→string formatter (__str_concat → __to_str → __static_str), a pure-int
146
+ // program ballooning 1 → ~19 funcs (see test/wat-invariants.js, .work/todo.md).
147
+ if (ta === VAL.BOOL && tb && tb !== VAL.BOOL) return tb
148
+ if (tb === VAL.BOOL && ta && ta !== VAL.BOOL) return ta
149
+ return null
141
150
  }
142
151
 
143
152
  // Value-preserving logical: `&&`/`||` return one of their operands.
@@ -162,6 +171,10 @@ VT['&&'] = VT['||'] = VT['??'] = (args) => {
162
171
  // Index access: `arr[i]` → ['[]', arr, i].
163
172
  VT['[]'] = (args) => {
164
173
  if (args.length < 2) return VAL.ARRAY
174
+ // A literal NEGATIVE index is always out of range → reads undefined, not the
175
+ // element type. Returning a numeric elem type here would let `a[-1] === undefined`
176
+ // fold to false (a NUMBER can't be undefined), silently dropping the guard.
177
+ { const li = intLiteralValue(args[1]); if (li != null && li < 0) return null }
165
178
  // SRoA flat-array slot read: `a[k]` (static index) where `a` dissolved into
166
179
  // scalar `a#i` locals (scanFlatObjects). A write-once slot's value-type is its
167
180
  // element literal's — same numeric-binding as the `VT['.']` object case, so
package/src/op-policy.js CHANGED
@@ -22,7 +22,10 @@ export const REJECT_OPS = {
22
22
  }
23
23
 
24
24
  /** Bare identifiers prepare rejects (no jzify lowering). */
25
- export const REJECT_IDENTS = {
25
+ // Prototype-less (Object.create(null)): a plain `{}` inherits Object.prototype in V8, so
26
+ // `REJECT_IDENTS['valueOf']` would return the inherited method (truthy) and wrongly reject
27
+ // a user identifier named like an Object method. Kernel objects are already prototype-less.
28
+ export const REJECT_IDENTS = Object.assign(Object.create(null), {
26
29
  with: '`with` not supported',
27
30
  class: '`class` not supported',
28
31
  yield: '`yield` not supported',
@@ -36,7 +39,7 @@ export const REJECT_IDENTS = {
36
39
  // identifier in sloppy JS (`var let = 5`), so rejecting it would refuse valid JS
37
40
  // (test262 language/expressions/object/let-non-strict-*).
38
41
  const: '`const` is a reserved word, not a valid name',
39
- }
42
+ })
40
43
 
41
44
  /** jzify-only errors for class lowering (no prepare counterpart). */
42
45
  export const JZIFY_CLASS_ERRORS = {
package/src/ops.js ADDED
@@ -0,0 +1,119 @@
1
+ // === AST op tags — integer-tagged-union representation ===
2
+ // internOps (prepare->compile boundary) converts array node[0] from op STRING to its
3
+ // integer tag; the compile half then dispatches via integer-keyed (eventually array)
4
+ // tables, removing the per-node string-hash lookup in the self-host kernel.
5
+ // OP: string -> int (1-based; 0 reserved => a missing tag is falsy). OPS: int -> string.
6
+ export const OP = {
7
+ "!": 1,
8
+ "!=": 2,
9
+ "!==": 3,
10
+ "%": 4,
11
+ "%=": 5,
12
+ "&": 6,
13
+ "&&": 7,
14
+ "&&=": 8,
15
+ "&=": 9,
16
+ "(": 10,
17
+ "()": 11,
18
+ "*": 12,
19
+ "**": 13,
20
+ "*=": 14,
21
+ "+": 15,
22
+ "++": 16,
23
+ "+=": 17,
24
+ ",": 18,
25
+ "-": 19,
26
+ "--": 20,
27
+ "-=": 21,
28
+ ".": 22,
29
+ "...": 23,
30
+ "/": 24,
31
+ "//": 25,
32
+ "/=": 26,
33
+ ";": 27,
34
+ "<": 28,
35
+ "<<": 29,
36
+ "<<=": 30,
37
+ "<=": 31,
38
+ "=": 32,
39
+ "==": 33,
40
+ "===": 34,
41
+ "=>": 35,
42
+ ">": 36,
43
+ ">=": 37,
44
+ ">>": 38,
45
+ ">>=": 39,
46
+ ">>>": 40,
47
+ ">>>=": 41,
48
+ "?": 42,
49
+ "?:": 43,
50
+ "??": 44,
51
+ "??=": 45,
52
+ "[": 46,
53
+ "[]": 47,
54
+ "^": 48,
55
+ "^=": 49,
56
+ "async": 50,
57
+ "await": 51,
58
+ "bigint": 52,
59
+ "block": 53,
60
+ "bool": 54,
61
+ "break": 55,
62
+ "call": 56,
63
+ "catch": 57,
64
+ "const": 58,
65
+ "continue": 59,
66
+ "default": 60,
67
+ "delete": 61,
68
+ "export": 62,
69
+ "finally": 63,
70
+ "for": 64,
71
+ "if": 65,
72
+ "import": 66,
73
+ "in": 67,
74
+ "instanceof": 68,
75
+ "label": 69,
76
+ "let": 70,
77
+ "nan": 71,
78
+ "new": 72,
79
+ "return": 73,
80
+ "spread": 74,
81
+ "str": 75,
82
+ "strcat": 76,
83
+ "switch": 77,
84
+ "throw": 78,
85
+ "typeof": 79,
86
+ "u+": 80,
87
+ "u-": 81,
88
+ "void": 82,
89
+ "while": 83,
90
+ "yield": 84,
91
+ "{": 85,
92
+ "{}": 86,
93
+ "|": 87,
94
+ "|=": 88,
95
+ "||": 89,
96
+ "||=": 90,
97
+ "~": 91,
98
+ }
99
+ export const OPS = [null, "!", "!=", "!==", "%", "%=", "&", "&&", "&&=", "&=", "(", "()", "*", "**", "*=", "+", "++", "+=", ",", "-", "--", "-=", ".", "...", "/", "//", "/=", ";", "<", "<<", "<<=", "<=", "=", "==", "===", "=>", ">", ">=", ">>", ">>=", ">>>", ">>>=", "?", "?:", "??", "??=", "[", "[]", "^", "^=", "async", "await", "bigint", "block", "bool", "break", "call", "catch", "const", "continue", "default", "delete", "export", "finally", "for", "if", "import", "in", "instanceof", "label", "let", "nan", "new", "return", "spread", "str", "strcat", "switch", "throw", "typeof", "u+", "u-", "void", "while", "yield", "{", "{}", "|", "|=", "||", "||=", "~"]
100
+ export const OP_COUNT = 92
101
+
102
+ // Normalize an op tag to its string form for op-Set membership / switch checks,
103
+ // so `SET.has(opStr(node[0]))` and `switch (opStr(op))` work whether node[0] is
104
+ // still a string (intern off / non-interned op like ':') or an integer (intern
105
+ // on). Self-host-safe: the typeof guard avoids indexing the OPS array by a string
106
+ // (jz arrays trap on non-integer indices), and it never grows a Set.
107
+ export const opStr = (op) => typeof op === "number" ? OPS[op] : op
108
+
109
+ // Convert array node[0] from op-STRING to integer tag, recursively. Once per node at
110
+ // the prepare boundary. Unknown op-strings stay strings (dual-keyed tables handle them
111
+ // until the int-only phase). node[0]===null (numeric literal) stays null; identifiers
112
+ // (bare strings at n[1+]) untouched.
113
+ export const internOps = (n) => {
114
+ if (!Array.isArray(n)) return n
115
+ const t = n[0]
116
+ if (typeof t === "string") { const id = OP[t]; if (id !== undefined) n[0] = id }
117
+ for (let i = 1; i < n.length; i++) if (Array.isArray(n[i])) internOps(n[i])
118
+ return n
119
+ }