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.
- package/README.md +37 -33
- package/bench/README.md +176 -73
- package/bench/bench.svg +58 -71
- package/cli.js +12 -5
- package/dist/interop.js +1 -1
- package/dist/jz.js +1366 -1101
- package/index.js +40 -9
- package/interop.js +193 -138
- package/layout.js +29 -18
- package/module/array.js +49 -73
- package/module/collection.js +83 -25
- package/module/console.js +1 -1
- package/module/core.js +161 -15
- package/module/json.js +3 -3
- package/module/math.js +167 -117
- package/module/number.js +247 -13
- package/module/object.js +11 -5
- package/module/regex.js +8 -7
- package/module/string.js +295 -171
- package/module/typedarray.js +169 -105
- package/package.json +7 -3
- package/src/abi/string.js +40 -35
- package/src/ast.js +19 -2
- package/src/compile/analyze.js +64 -2
- package/src/compile/cse-load.js +200 -0
- package/src/compile/emit-assign.js +73 -14
- package/src/compile/emit.js +324 -34
- package/src/compile/index.js +204 -61
- package/src/compile/infer.js +8 -1
- package/src/compile/loop-divmod.js +12 -58
- package/src/compile/loop-model.js +91 -0
- package/src/compile/loop-recurrence.js +167 -0
- package/src/compile/loop-square.js +102 -0
- package/src/compile/narrow.js +180 -34
- package/src/compile/peel-stencil.js +18 -64
- package/src/compile/plan/common.js +29 -0
- package/src/compile/plan/index.js +4 -1
- package/src/compile/plan/inline.js +176 -21
- package/src/compile/plan/literals.js +93 -19
- package/src/ctx.js +51 -12
- package/src/helper-counters.js +137 -0
- package/src/ir.js +102 -13
- package/src/kind-traits.js +7 -3
- package/src/kind.js +14 -1
- package/src/op-policy.js +5 -2
- package/src/ops.js +119 -0
- package/src/optimize/index.js +1125 -136
- package/src/optimize/recurse.js +182 -0
- package/src/optimize/vectorize.js +1302 -144
- package/src/prepare/index.js +29 -12
- package/src/reps.js +4 -1
- package/src/type.js +53 -45
- package/src/wat/assemble.js +92 -9
- package/src/widen.js +21 -0
- package/src/wat/optimize.js +0 -3938
package/src/kind-traits.js
CHANGED
|
@@ -5,9 +5,13 @@
|
|
|
5
5
|
|
|
6
6
|
import { VAL } from './reps.js'
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
+
}
|