@scriptc/compiler 0.3.0 → 0.3.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/ambient/scriptc-node-fallback.d.ts +5 -2
- package/dist/backend/llvm/bigint-wrap.d.ts +4 -0
- package/dist/backend/llvm/bigint-wrap.js +590 -0
- package/dist/backend/llvm/bigint-wrap.js.map +1 -0
- package/dist/backend/llvm/blocks.d.ts +2 -0
- package/dist/backend/llvm/blocks.js +4 -0
- package/dist/backend/llvm/blocks.js.map +1 -1
- package/dist/backend/llvm/call-lifetimes.js +15 -4
- package/dist/backend/llvm/call-lifetimes.js.map +1 -1
- package/dist/backend/llvm/call-lifetimes.test.js +14 -3
- package/dist/backend/llvm/call-lifetimes.test.js.map +1 -1
- package/dist/backend/llvm/checked-narrows.test.js +1 -1
- package/dist/backend/llvm/checked-narrows.test.js.map +1 -1
- package/dist/backend/llvm/classes.js +57 -12
- package/dist/backend/llvm/classes.js.map +1 -1
- package/dist/backend/llvm/classes.test.js +4 -4
- package/dist/backend/llvm/classes.test.js.map +1 -1
- package/dist/backend/llvm/common.d.ts +7 -5
- package/dist/backend/llvm/common.js +7 -5
- package/dist/backend/llvm/common.js.map +1 -1
- package/dist/backend/llvm/constant-callbacks.js +10 -28
- package/dist/backend/llvm/constant-callbacks.js.map +1 -1
- package/dist/backend/llvm/dense-array-access.js +133 -45
- package/dist/backend/llvm/dense-array-access.js.map +1 -1
- package/dist/backend/llvm/dense-array-access.test.js +53 -1
- package/dist/backend/llvm/dense-array-access.test.js.map +1 -1
- package/dist/backend/llvm/dyn.d.ts +2 -0
- package/dist/backend/llvm/dyn.js +4 -0
- package/dist/backend/llvm/dyn.js.map +1 -1
- package/dist/backend/llvm/emitter.d.ts +120 -6
- package/dist/backend/llvm/emitter.js +676 -77
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/backend/llvm/emitter.test.js +14 -7
- package/dist/backend/llvm/emitter.test.js.map +1 -1
- package/dist/backend/llvm/expr-async.js +120 -43
- package/dist/backend/llvm/expr-async.js.map +1 -1
- package/dist/backend/llvm/expr-async.test.js +1 -1
- package/dist/backend/llvm/expr-async.test.js.map +1 -1
- package/dist/backend/llvm/expr-bytes.js +51 -36
- package/dist/backend/llvm/expr-bytes.js.map +1 -1
- package/dist/backend/llvm/expr-callbacks.js +72 -3
- package/dist/backend/llvm/expr-callbacks.js.map +1 -1
- package/dist/backend/llvm/expr-calls.d.ts +1 -1
- package/dist/backend/llvm/expr-calls.js +159 -30
- package/dist/backend/llvm/expr-calls.js.map +1 -1
- package/dist/backend/llvm/expr-calls.test.d.ts +1 -0
- package/dist/backend/llvm/expr-calls.test.js +132 -0
- package/dist/backend/llvm/expr-calls.test.js.map +1 -0
- package/dist/backend/llvm/expr-containers.js +13 -0
- package/dist/backend/llvm/expr-containers.js.map +1 -1
- package/dist/backend/llvm/expr-context.d.ts +2 -0
- package/dist/backend/llvm/expr-dispatch.js +3 -0
- package/dist/backend/llvm/expr-dispatch.js.map +1 -1
- package/dist/backend/llvm/expr-dynamic.js +26 -4
- package/dist/backend/llvm/expr-dynamic.js.map +1 -1
- package/dist/backend/llvm/expr-primitives.d.ts +7 -0
- package/dist/backend/llvm/expr-primitives.js +80 -22
- package/dist/backend/llvm/expr-primitives.js.map +1 -1
- package/dist/backend/llvm/expr-primitives.test.js +95 -5
- package/dist/backend/llvm/expr-primitives.test.js.map +1 -1
- package/dist/backend/llvm/expr-records.d.ts +9 -0
- package/dist/backend/llvm/expr-records.js +134 -16
- package/dist/backend/llvm/expr-records.js.map +1 -1
- package/dist/backend/llvm/expr-stream-bridges.d.ts +1 -1
- package/dist/backend/llvm/expr-stream-bridges.js +80 -1
- package/dist/backend/llvm/expr-stream-bridges.js.map +1 -1
- package/dist/backend/llvm/expr-stream-bridges.test.js +50 -1
- package/dist/backend/llvm/expr-stream-bridges.test.js.map +1 -1
- package/dist/backend/llvm/int32-abi.d.ts +31 -0
- package/dist/backend/llvm/int32-abi.js +62 -0
- package/dist/backend/llvm/int32-abi.js.map +1 -0
- package/dist/backend/llvm/int32-abi.test.d.ts +1 -0
- package/dist/backend/llvm/int32-abi.test.js +107 -0
- package/dist/backend/llvm/int32-abi.test.js.map +1 -0
- package/dist/backend/llvm/int32-locals.d.ts +30 -0
- package/dist/backend/llvm/int32-locals.js +95 -0
- package/dist/backend/llvm/int32-locals.js.map +1 -0
- package/dist/backend/llvm/integer-values.d.ts +4 -0
- package/dist/backend/llvm/integer-values.js +30 -1
- package/dist/backend/llvm/integer-values.js.map +1 -1
- package/dist/backend/llvm/integer-views.d.ts +8 -1
- package/dist/backend/llvm/integer-views.js +47 -2
- package/dist/backend/llvm/integer-views.js.map +1 -1
- package/dist/backend/llvm/lazy-captures.d.ts +16 -0
- package/dist/backend/llvm/lazy-captures.js +88 -2
- package/dist/backend/llvm/lazy-captures.js.map +1 -1
- package/dist/backend/llvm/lazy-captures.test.js +48 -0
- package/dist/backend/llvm/lazy-captures.test.js.map +1 -1
- package/dist/backend/llvm/lib-dispatch.js +14 -0
- package/dist/backend/llvm/lib-dispatch.js.map +1 -1
- package/dist/backend/llvm/lib-filesystem.js +3 -0
- package/dist/backend/llvm/lib-filesystem.js.map +1 -1
- package/dist/backend/llvm/lib-shared.js +7 -0
- package/dist/backend/llvm/lib-shared.js.map +1 -1
- package/dist/backend/llvm/local-array-reads.js +21 -2
- package/dist/backend/llvm/local-array-reads.js.map +1 -1
- package/dist/backend/llvm/local-array-reads.test.js +3 -2
- package/dist/backend/llvm/local-array-reads.test.js.map +1 -1
- package/dist/backend/llvm/local-union-storage.test.js +2 -2
- package/dist/backend/llvm/local-union-storage.test.js.map +1 -1
- package/dist/backend/llvm/map-read-lifetimes.test.js +2 -2
- package/dist/backend/llvm/map-read-lifetimes.test.js.map +1 -1
- package/dist/backend/llvm/reference-effects.js +2 -0
- package/dist/backend/llvm/reference-effects.js.map +1 -1
- package/dist/backend/llvm/reference-writes.d.ts +3 -0
- package/dist/backend/llvm/reference-writes.js +10 -2
- package/dist/backend/llvm/reference-writes.js.map +1 -1
- package/dist/backend/llvm/regex-matchers.d.ts +39 -0
- package/dist/backend/llvm/regex-matchers.js +1649 -0
- package/dist/backend/llvm/regex-matchers.js.map +1 -0
- package/dist/backend/llvm/regex-matchers.test.d.ts +1 -0
- package/dist/backend/llvm/regex-matchers.test.js +96 -0
- package/dist/backend/llvm/regex-matchers.test.js.map +1 -0
- package/dist/backend/llvm/shapes.d.ts +36 -1
- package/dist/backend/llvm/shapes.js +127 -14
- package/dist/backend/llvm/shapes.js.map +1 -1
- package/dist/backend/llvm/stack-unions.test.js +1 -1
- package/dist/backend/llvm/stack-unions.test.js.map +1 -1
- package/dist/backend/llvm/string-construction.test.js +2 -2
- package/dist/backend/llvm/string-construction.test.js.map +1 -1
- package/dist/backend/llvm/string-lifetimes.test.js +5 -5
- package/dist/backend/llvm/string-lifetimes.test.js.map +1 -1
- package/dist/backend/llvm/string-literal-equality.d.ts +22 -0
- package/dist/backend/llvm/string-literal-equality.js +78 -0
- package/dist/backend/llvm/string-literal-equality.js.map +1 -0
- package/dist/backend/llvm/switch-dispatch.js +59 -13
- package/dist/backend/llvm/switch-dispatch.js.map +1 -1
- package/dist/backend/llvm/switch-dispatch.test.js +71 -6
- package/dist/backend/llvm/switch-dispatch.test.js.map +1 -1
- package/dist/backend/llvm/union-retype.test.d.ts +1 -0
- package/dist/backend/llvm/union-retype.test.js +229 -0
- package/dist/backend/llvm/union-retype.test.js.map +1 -0
- package/dist/backend/llvm/wrapping-sums.d.ts +17 -0
- package/dist/backend/llvm/wrapping-sums.js +113 -0
- package/dist/backend/llvm/wrapping-sums.js.map +1 -0
- package/dist/backend/loop-polls.js +1 -0
- package/dist/backend/loop-polls.js.map +1 -1
- package/dist/backend/mangle.d.ts +9 -0
- package/dist/backend/mangle.js +15 -0
- package/dist/backend/mangle.js.map +1 -1
- package/dist/backend/may-throw.js +6 -3
- package/dist/backend/may-throw.js.map +1 -1
- package/dist/backend/runtime-abi.d.ts +2 -2
- package/dist/backend/runtime-abi.js +2 -2
- package/dist/backend/runtime-pack-targets.test.js +1 -1
- package/dist/backend/runtime-pack.test.js +1 -1
- package/dist/contracts/check.js +4 -0
- package/dist/contracts/check.js.map +1 -1
- package/dist/frontend/lowering/array-values.d.ts +14 -0
- package/dist/frontend/lowering/array-values.js +29 -0
- package/dist/frontend/lowering/array-values.js.map +1 -1
- package/dist/frontend/lowering/containers/array-callbacks.js +40 -0
- package/dist/frontend/lowering/containers/array-callbacks.js.map +1 -1
- package/dist/frontend/lowering/containers/array-pipelines.d.ts +54 -0
- package/dist/frontend/lowering/containers/array-pipelines.js +1827 -0
- package/dist/frontend/lowering/containers/array-pipelines.js.map +1 -0
- package/dist/frontend/lowering/containers/exec-result-uses.d.ts +10 -0
- package/dist/frontend/lowering/containers/exec-result-uses.js +88 -0
- package/dist/frontend/lowering/containers/exec-result-uses.js.map +1 -0
- package/dist/frontend/lowering/containers/string-and-regexp.js +15 -7
- package/dist/frontend/lowering/containers/string-and-regexp.js.map +1 -1
- package/dist/frontend/lowering/containers/string-replacement.d.ts +2 -1
- package/dist/frontend/lowering/containers/string-replacement.js +32 -136
- package/dist/frontend/lowering/containers/string-replacement.js.map +1 -1
- package/dist/frontend/lowering/devirtualize.d.ts +33 -0
- package/dist/frontend/lowering/devirtualize.js +199 -0
- package/dist/frontend/lowering/devirtualize.js.map +1 -0
- package/dist/frontend/lowering/expressions/nullish-assignment.js +55 -7
- package/dist/frontend/lowering/expressions/nullish-assignment.js.map +1 -1
- package/dist/frontend/lowering/identifier-uses.d.ts +7 -0
- package/dist/frontend/lowering/identifier-uses.js +23 -0
- package/dist/frontend/lowering/identifier-uses.js.map +1 -0
- package/dist/frontend/lowering/iterator-adapters.js +51 -3
- package/dist/frontend/lowering/iterator-adapters.js.map +1 -1
- package/dist/frontend/lowering/lower-array-sort.js +13 -0
- package/dist/frontend/lowering/lower-array-sort.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.js +64 -30
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-classes.js +1 -11
- package/dist/frontend/lowering/lower-classes.js.map +1 -1
- package/dist/frontend/lowering/lower-console.d.ts +14 -0
- package/dist/frontend/lowering/lower-console.js +195 -0
- package/dist/frontend/lowering/lower-console.js.map +1 -0
- package/dist/frontend/lowering/lower-exprs.js +135 -3
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lower-functions.js +7 -3
- package/dist/frontend/lowering/lower-functions.js.map +1 -1
- package/dist/frontend/lowering/lower-generators.d.ts +10 -0
- package/dist/frontend/lowering/lower-generators.js +108 -41
- package/dist/frontend/lowering/lower-generators.js.map +1 -1
- package/dist/frontend/lowering/lower-modules.js +4 -0
- package/dist/frontend/lowering/lower-modules.js.map +1 -1
- package/dist/frontend/lowering/lower-stream.js +13 -10
- package/dist/frontend/lowering/lower-stream.js.map +1 -1
- package/dist/frontend/lowering/lowerer.d.ts +25 -4
- package/dist/frontend/lowering/lowerer.js +120 -47
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/lowering/nan-coded-reads.d.ts +16 -0
- package/dist/frontend/lowering/nan-coded-reads.js +108 -1
- package/dist/frontend/lowering/nan-coded-reads.js.map +1 -1
- package/dist/frontend/lowering/runtime-optional-bounds.d.ts +4 -0
- package/dist/frontend/lowering/runtime-optional-bounds.js +2 -0
- package/dist/frontend/lowering/runtime-optional-bounds.js.map +1 -1
- package/dist/frontend/lowering/runtime-optional-bounds.test.js +27 -0
- package/dist/frontend/lowering/runtime-optional-bounds.test.js.map +1 -1
- package/dist/frontend/lowering/split-consumers.js +2 -18
- package/dist/frontend/lowering/split-consumers.js.map +1 -1
- package/dist/frontend/lowering/surfaces.js +3 -8
- package/dist/frontend/lowering/surfaces.js.map +1 -1
- package/dist/frontend/lowering/union-retag.d.ts +6 -1
- package/dist/frontend/lowering/union-retag.js +64 -1
- package/dist/frontend/lowering/union-retag.js.map +1 -1
- package/dist/frontend/lowering/union-retag.test.js +46 -0
- package/dist/frontend/lowering/union-retag.test.js.map +1 -1
- package/dist/frontend/type-mapper.js +16 -3
- package/dist/frontend/type-mapper.js.map +1 -1
- package/dist/ir/builtin-effects.js +2 -0
- package/dist/ir/builtin-effects.js.map +1 -1
- package/dist/ir/builtin-signatures.d.ts +1 -1
- package/dist/ir/builtin-signatures.js +12 -1
- package/dist/ir/builtin-signatures.js.map +1 -1
- package/dist/ir/function-references.d.ts +12 -0
- package/dist/ir/function-references.js +29 -0
- package/dist/ir/function-references.js.map +1 -0
- package/dist/ir/function-references.test.d.ts +1 -0
- package/dist/ir/function-references.test.js +276 -0
- package/dist/ir/function-references.test.js.map +1 -0
- package/dist/ir/int32-slots.d.ts +7 -4
- package/dist/ir/int32-slots.js +214 -49
- package/dist/ir/int32-slots.js.map +1 -1
- package/dist/ir/int32-slots.test.js +38 -1
- package/dist/ir/int32-slots.test.js.map +1 -1
- package/dist/ir/integer-ranges.d.ts +4 -4
- package/dist/ir/integer-ranges.js +89 -64
- package/dist/ir/integer-ranges.js.map +1 -1
- package/dist/ir/integer-ranges.test.js +71 -9
- package/dist/ir/integer-ranges.test.js.map +1 -1
- package/dist/ir/intrinsic-signatures.js +1 -0
- package/dist/ir/intrinsic-signatures.js.map +1 -1
- package/dist/ir/ir.d.ts +52 -6
- package/dist/ir/ir.js +1 -0
- package/dist/ir/ir.js.map +1 -1
- package/dist/ir/runtime-features.js +3 -2
- package/dist/ir/runtime-features.js.map +1 -1
- package/dist/ir/traverse.js +4 -0
- package/dist/ir/traverse.js.map +1 -1
- package/dist/ir/validate.js +57 -6
- package/dist/ir/validate.js.map +1 -1
- package/dist/ir/validate.test.js +25 -7
- package/dist/ir/validate.test.js.map +1 -1
- package/package.json +20 -20
- package/surface-manifest.json +1 -17
|
@@ -113,10 +113,13 @@ declare const console: {
|
|
|
113
113
|
timeLog(label?: string, ...data: unknown[]): void;
|
|
114
114
|
timeStamp(label?: string): void;
|
|
115
115
|
trace(...data: unknown[]): void;
|
|
116
|
+
Console: typeof import("node:console").Console;
|
|
116
117
|
};
|
|
117
118
|
|
|
118
|
-
// node:console exports the global instance's bound methods
|
|
119
|
-
//
|
|
119
|
+
// node:console exports the global instance's bound methods and the Console
|
|
120
|
+
// constructor: instances over process.stdout/process.stderr or node:stream
|
|
121
|
+
// writables lower their log/info/debug/error/warn members; the remaining
|
|
122
|
+
// methods are declared so unsupported uses reach named fences.
|
|
120
123
|
type ScriptcConsoleMethods = typeof console;
|
|
121
124
|
declare module "node:console" {
|
|
122
125
|
export class Console {
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { LibCallExpr, LlValue, LlvmEmitterContext } from "./expr-context.js";
|
|
2
|
+
/** Emits a small-value bigint tree, or returns null when `e` is not one
|
|
3
|
+
* (the caller then emits the plain library call). */
|
|
4
|
+
export declare function emitBigIntWrap(host: LlvmEmitterContext, e: LibCallExpr): LlValue | null;
|
|
@@ -0,0 +1,590 @@
|
|
|
1
|
+
import { emitBorrowedInput } from "./borrowed-inputs.js";
|
|
2
|
+
const BINARY = {
|
|
3
|
+
"bigint.add": "+",
|
|
4
|
+
"bigint.sub": "-",
|
|
5
|
+
"bigint.mul": "*",
|
|
6
|
+
"bigint.and": "&",
|
|
7
|
+
"bigint.or": "|",
|
|
8
|
+
"bigint.xor": "^",
|
|
9
|
+
"bigint.div": "/",
|
|
10
|
+
"bigint.mod": "%",
|
|
11
|
+
};
|
|
12
|
+
const LL_BINARY = {
|
|
13
|
+
"&": "and",
|
|
14
|
+
"|": "or",
|
|
15
|
+
"^": "xor",
|
|
16
|
+
};
|
|
17
|
+
/** The value of a bigint literal (or constant BigInt("...") text). */
|
|
18
|
+
function literalValue(e) {
|
|
19
|
+
if (e.kind !== "libCall" || e.fn !== "bigint.parse")
|
|
20
|
+
return null;
|
|
21
|
+
const text = e.args[0];
|
|
22
|
+
if (text?.kind !== "strLit")
|
|
23
|
+
return null;
|
|
24
|
+
try {
|
|
25
|
+
return BigInt(text.value);
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/** A narrowing width k in 1..64 given as a numeric literal. */
|
|
32
|
+
function narrowBits(e) {
|
|
33
|
+
if (e?.kind !== "numLit")
|
|
34
|
+
return null;
|
|
35
|
+
const k = e.value;
|
|
36
|
+
return Number.isInteger(k) && k >= 1 && k <= 64 ? k : null;
|
|
37
|
+
}
|
|
38
|
+
/** Reads and arithmetic on them: no effects, no throws. */
|
|
39
|
+
function pureExpr(e) {
|
|
40
|
+
switch (e.kind) {
|
|
41
|
+
case "varRef":
|
|
42
|
+
case "numLit":
|
|
43
|
+
return true;
|
|
44
|
+
case "bin":
|
|
45
|
+
return pureExpr(e.left) && pureExpr(e.right);
|
|
46
|
+
case "unary":
|
|
47
|
+
return pureExpr(e.operand);
|
|
48
|
+
default:
|
|
49
|
+
return literalValue(e) !== null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/** The root's mode, outer wrap and tree, or null when nothing applies. */
|
|
53
|
+
function rootOf(e) {
|
|
54
|
+
if (e.fn === "bigint.asUintN" || e.fn === "bigint.asIntN") {
|
|
55
|
+
const bits = narrowBits(e.args[0]);
|
|
56
|
+
if (bits === null)
|
|
57
|
+
return null;
|
|
58
|
+
return { mode: "wrap", wrap: { signed: e.fn === "bigint.asIntN", bits }, operand: e.args[1] };
|
|
59
|
+
}
|
|
60
|
+
if (e.fn === "bigint.and") {
|
|
61
|
+
// x & M with a literal 0 <= M < 2^64 keeps only low bits of x, and the
|
|
62
|
+
// result is non-negative.
|
|
63
|
+
for (const side of [1, 0]) {
|
|
64
|
+
const m = literalValue(e.args[side]);
|
|
65
|
+
if (m !== null && m >= 0n && m < 1n << 64n)
|
|
66
|
+
return { mode: "wrap", wrap: null, operand: e };
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (BINARY[e.fn] !== undefined ||
|
|
70
|
+
e.fn === "bigint.shl" ||
|
|
71
|
+
e.fn === "bigint.shr" ||
|
|
72
|
+
e.fn === "bigint.neg" ||
|
|
73
|
+
e.fn === "bigint.not") {
|
|
74
|
+
return { mode: "exact", wrap: null, operand: e };
|
|
75
|
+
}
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
/** A literal whose magnitude needs more than `bits` bits. */
|
|
79
|
+
function wideConstant(e, bits) {
|
|
80
|
+
const value = literalValue(e);
|
|
81
|
+
return value !== null && (value < 0n ? -value : value) >> bits !== 0n;
|
|
82
|
+
}
|
|
83
|
+
/** A shift count literal in 0..63. */
|
|
84
|
+
function shiftCount(e) {
|
|
85
|
+
const count = literalValue(e);
|
|
86
|
+
return count !== null && count >= 0n && count < 64n ? Number(count) : null;
|
|
87
|
+
}
|
|
88
|
+
class TreeBuilder {
|
|
89
|
+
mode;
|
|
90
|
+
dedupe;
|
|
91
|
+
/** Bigint leaves (literals included) and BigInt(number) operands. */
|
|
92
|
+
leaves = [];
|
|
93
|
+
numbers = [];
|
|
94
|
+
/** Postfix program for the exact fallback (scr_bigint_wrap_eval). */
|
|
95
|
+
program = [];
|
|
96
|
+
/** Operations, counting BigInt(number) conversions. */
|
|
97
|
+
operations = 0;
|
|
98
|
+
/** Every leaf is pure (pureExpr). */
|
|
99
|
+
pure = true;
|
|
100
|
+
ok = true;
|
|
101
|
+
/** Leaves an exact operation reads: they must fit in 64 bits. Leaves
|
|
102
|
+
* below a narrowing only contribute their low 64 bits. */
|
|
103
|
+
strict = new Set();
|
|
104
|
+
/** Local reads already placed, by local id (only with `dedupe`). */
|
|
105
|
+
reads = new Map();
|
|
106
|
+
/** With `dedupe`, repeated reads of one local share a leaf: correct only
|
|
107
|
+
* when no leaf can write it, which a first pass establishes (`pure`).
|
|
108
|
+
* The mode switches to fixed width below each asUintN/asIntN. */
|
|
109
|
+
constructor(mode, dedupe) {
|
|
110
|
+
this.mode = mode;
|
|
111
|
+
this.dedupe = dedupe;
|
|
112
|
+
}
|
|
113
|
+
/** Leaves are evaluated before every operation. Each leaf after the
|
|
114
|
+
* first operation must be pure, so an operation that throws (division
|
|
115
|
+
* by zero) still throws before anything a later operand would do. */
|
|
116
|
+
place(e) {
|
|
117
|
+
if (!pureExpr(e)) {
|
|
118
|
+
this.pure = false;
|
|
119
|
+
if (this.operations > 0)
|
|
120
|
+
this.ok = false;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
leaf(e) {
|
|
124
|
+
const value = literalValue(e);
|
|
125
|
+
if (value !== null) {
|
|
126
|
+
// Exact trees hold every magnitude in 64 bits.
|
|
127
|
+
if (this.mode === "exact" && (value < 0n ? -value : value) >= 1n << 64n)
|
|
128
|
+
this.ok = false;
|
|
129
|
+
const index = this.leaves.length;
|
|
130
|
+
this.leaves.push(e);
|
|
131
|
+
this.program.push(`L${index}`);
|
|
132
|
+
return { kind: "const", value };
|
|
133
|
+
}
|
|
134
|
+
this.place(e);
|
|
135
|
+
if (this.dedupe && e.kind === "varRef") {
|
|
136
|
+
const seen = this.reads.get(e.localId);
|
|
137
|
+
if (seen !== undefined) {
|
|
138
|
+
if (this.mode === "exact")
|
|
139
|
+
this.strict.add(seen);
|
|
140
|
+
this.program.push(`L${seen}`);
|
|
141
|
+
return { kind: "leaf", index: seen };
|
|
142
|
+
}
|
|
143
|
+
this.reads.set(e.localId, this.leaves.length);
|
|
144
|
+
}
|
|
145
|
+
const index = this.leaves.length;
|
|
146
|
+
this.leaves.push(e);
|
|
147
|
+
if (this.mode === "exact")
|
|
148
|
+
this.strict.add(index);
|
|
149
|
+
this.program.push(`L${index}`);
|
|
150
|
+
return { kind: "leaf", index };
|
|
151
|
+
}
|
|
152
|
+
op(token) {
|
|
153
|
+
this.program.push(token);
|
|
154
|
+
this.operations++;
|
|
155
|
+
}
|
|
156
|
+
node(e, depth) {
|
|
157
|
+
if (depth > 24 || this.leaves.length + this.numbers.length > 24) {
|
|
158
|
+
this.ok = false;
|
|
159
|
+
return this.leaf(e);
|
|
160
|
+
}
|
|
161
|
+
if (e.kind !== "libCall")
|
|
162
|
+
return this.leaf(e);
|
|
163
|
+
const exact = this.mode === "exact";
|
|
164
|
+
const op = BINARY[e.fn];
|
|
165
|
+
if (op !== undefined && e.args.length === 2 && (exact || (op !== "/" && op !== "%"))) {
|
|
166
|
+
// An exact product with a wide constant (a hash multiplier) almost
|
|
167
|
+
// always leaves 64 bits; the plain operators are faster than taking
|
|
168
|
+
// the fallback every time.
|
|
169
|
+
if (exact && op === "*" && e.args.some((arg) => wideConstant(arg, 32n)))
|
|
170
|
+
this.ok = false;
|
|
171
|
+
const left = this.node(e.args[0], depth + 1);
|
|
172
|
+
const right = this.node(e.args[1], depth + 1);
|
|
173
|
+
this.op(op);
|
|
174
|
+
return { kind: "bin", op, left, right };
|
|
175
|
+
}
|
|
176
|
+
if ((e.fn === "bigint.shl" || (exact && e.fn === "bigint.shr")) && e.args.length === 2) {
|
|
177
|
+
const count = shiftCount(e.args[1]);
|
|
178
|
+
if (exact && e.fn === "bigint.shl" && count !== null && count >= 32)
|
|
179
|
+
this.ok = false;
|
|
180
|
+
if (count !== null) {
|
|
181
|
+
const operand = this.node(e.args[0], depth + 1);
|
|
182
|
+
const left = e.fn === "bigint.shl";
|
|
183
|
+
this.op(`${left ? "<" : ">"}${count}`);
|
|
184
|
+
return { kind: "shift", left, count, operand };
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
if ((e.fn === "bigint.neg" || e.fn === "bigint.not") && e.args.length === 1) {
|
|
188
|
+
const operand = this.node(e.args[0], depth + 1);
|
|
189
|
+
this.op(e.fn === "bigint.neg" ? "n" : "~");
|
|
190
|
+
return { kind: e.fn === "bigint.neg" ? "neg" : "not", operand };
|
|
191
|
+
}
|
|
192
|
+
if (e.fn === "bigint.asUintN" || e.fn === "bigint.asIntN") {
|
|
193
|
+
const bits = narrowBits(e.args[0]);
|
|
194
|
+
if (bits !== null) {
|
|
195
|
+
const outer = this.mode;
|
|
196
|
+
this.mode = "wrap";
|
|
197
|
+
const operand = this.node(e.args[1], depth + 1);
|
|
198
|
+
this.mode = outer;
|
|
199
|
+
const signed = e.fn === "bigint.asIntN";
|
|
200
|
+
this.op(`${signed ? "i" : "u"}${bits}`);
|
|
201
|
+
return { kind: "wrap", signed, bits, operand };
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
if (e.fn === "bigint.fromF64" && e.args.length === 1) {
|
|
205
|
+
// The conversion (and its RangeError) runs at this point of the
|
|
206
|
+
// program; only the number is evaluated with the leaves.
|
|
207
|
+
const value = e.args[0];
|
|
208
|
+
this.place(value);
|
|
209
|
+
const index = this.numbers.length;
|
|
210
|
+
this.numbers.push(value);
|
|
211
|
+
this.program.push(`F${index}`);
|
|
212
|
+
this.operations++;
|
|
213
|
+
return { kind: "number", index };
|
|
214
|
+
}
|
|
215
|
+
return this.leaf(e);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
/** i64 constant text for the low 64 bits of a bigint. */
|
|
219
|
+
function i64Const(value) {
|
|
220
|
+
return BigInt.asIntN(64, value).toString();
|
|
221
|
+
}
|
|
222
|
+
class TreeEmitter {
|
|
223
|
+
host;
|
|
224
|
+
leafValues;
|
|
225
|
+
numberValues;
|
|
226
|
+
/** i1 conditions that send the tree to the exact fallback. */
|
|
227
|
+
bad = [];
|
|
228
|
+
constructor(host, leafValues, numberValues) {
|
|
229
|
+
this.host = host;
|
|
230
|
+
this.leafValues = leafValues;
|
|
231
|
+
this.numberValues = numberValues;
|
|
232
|
+
}
|
|
233
|
+
tmp(line) {
|
|
234
|
+
const t = this.host.B.tmp();
|
|
235
|
+
this.host.B.line(line(t));
|
|
236
|
+
return t;
|
|
237
|
+
}
|
|
238
|
+
/** `value` narrowed to k bits within an i64: zero- or sign-extended. */
|
|
239
|
+
narrow(value, signed, bits) {
|
|
240
|
+
if (bits >= 64)
|
|
241
|
+
return value;
|
|
242
|
+
if (!signed) {
|
|
243
|
+
return this.tmp((t) => `${t} = and i64 ${value}, ${i64Const((1n << BigInt(bits)) - 1n)}`);
|
|
244
|
+
}
|
|
245
|
+
const up = this.tmp((t) => `${t} = shl i64 ${value}, ${64 - bits}`);
|
|
246
|
+
return this.tmp((t) => `${t} = ashr i64 ${up}, ${64 - bits}`);
|
|
247
|
+
}
|
|
248
|
+
/** Fixed width: everything modulo 2^64 in two's complement. */
|
|
249
|
+
wrap(node) {
|
|
250
|
+
switch (node.kind) {
|
|
251
|
+
case "leaf":
|
|
252
|
+
return this.leafValues[node.index].bits;
|
|
253
|
+
case "number":
|
|
254
|
+
return this.numberValues[node.index].bits;
|
|
255
|
+
case "const":
|
|
256
|
+
return i64Const(node.value);
|
|
257
|
+
case "bin": {
|
|
258
|
+
const l = this.wrap(node.left);
|
|
259
|
+
const r = this.wrap(node.right);
|
|
260
|
+
const op = { "+": "add", "-": "sub", "*": "mul", "&": "and", "|": "or", "^": "xor" }[node.op];
|
|
261
|
+
return this.tmp((t) => `${t} = ${op} i64 ${l}, ${r}`);
|
|
262
|
+
}
|
|
263
|
+
case "shift": {
|
|
264
|
+
const v = this.wrap(node.operand);
|
|
265
|
+
return this.tmp((t) => `${t} = shl i64 ${v}, ${node.count}`);
|
|
266
|
+
}
|
|
267
|
+
case "neg": {
|
|
268
|
+
const v = this.wrap(node.operand);
|
|
269
|
+
return this.tmp((t) => `${t} = sub i64 0, ${v}`);
|
|
270
|
+
}
|
|
271
|
+
case "not": {
|
|
272
|
+
const v = this.wrap(node.operand);
|
|
273
|
+
return this.tmp((t) => `${t} = xor i64 ${v}, -1`);
|
|
274
|
+
}
|
|
275
|
+
case "wrap":
|
|
276
|
+
return this.narrow(this.wrap(node.operand), node.signed, node.bits);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
/** Sign-magnitude result with zero kept non-negative. */
|
|
280
|
+
signed(neg, mag) {
|
|
281
|
+
const nonzero = this.tmp((t) => `${t} = icmp ne i64 ${mag}, 0`);
|
|
282
|
+
return { neg: this.tmp((t) => `${t} = and i1 ${neg}, ${nonzero}`), mag };
|
|
283
|
+
}
|
|
284
|
+
overflowing(fn, l, r) {
|
|
285
|
+
this.host.declare(`declare { i64, i1 } @llvm.${fn}.with.overflow.i64(i64, i64)`);
|
|
286
|
+
const pair = this.tmp((t) => `${t} = call { i64, i1 } @llvm.${fn}.with.overflow.i64(i64 ${l}, i64 ${r})`);
|
|
287
|
+
this.bad.push(this.tmp((t) => `${t} = extractvalue { i64, i1 } ${pair}, 1`));
|
|
288
|
+
return this.tmp((t) => `${t} = extractvalue { i64, i1 } ${pair}, 0`);
|
|
289
|
+
}
|
|
290
|
+
/** ±(a.mag) + ±(b.mag), failing when the magnitude reaches 2^64. */
|
|
291
|
+
add(a, b) {
|
|
292
|
+
const same = this.tmp((t) => `${t} = icmp eq i1 ${a.neg}, ${b.neg}`);
|
|
293
|
+
const sum = this.overflowing("uadd", a.mag, b.mag);
|
|
294
|
+
// A carry only matters when the signs agree.
|
|
295
|
+
const carry = this.bad.pop();
|
|
296
|
+
this.bad.push(this.tmp((t) => `${t} = and i1 ${carry}, ${same}`));
|
|
297
|
+
const aBig = this.tmp((t) => `${t} = icmp uge i64 ${a.mag}, ${b.mag}`);
|
|
298
|
+
const ab = this.tmp((t) => `${t} = sub i64 ${a.mag}, ${b.mag}`);
|
|
299
|
+
const ba = this.tmp((t) => `${t} = sub i64 ${b.mag}, ${a.mag}`);
|
|
300
|
+
const diff = this.tmp((t) => `${t} = select i1 ${aBig}, i64 ${ab}, i64 ${ba}`);
|
|
301
|
+
const diffNeg = this.tmp((t) => `${t} = select i1 ${aBig}, i1 ${a.neg}, i1 ${b.neg}`);
|
|
302
|
+
const mag = this.tmp((t) => `${t} = select i1 ${same}, i64 ${sum}, i64 ${diff}`);
|
|
303
|
+
const neg = this.tmp((t) => `${t} = select i1 ${same}, i1 ${a.neg}, i1 ${diffNeg}`);
|
|
304
|
+
return this.signed(neg, mag);
|
|
305
|
+
}
|
|
306
|
+
negate(a) {
|
|
307
|
+
return this.signed(this.tmp((t) => `${t} = xor i1 ${a.neg}, true`), a.mag);
|
|
308
|
+
}
|
|
309
|
+
/** Exact: sign and magnitude below 2^64; anything else falls back. */
|
|
310
|
+
exact(node) {
|
|
311
|
+
switch (node.kind) {
|
|
312
|
+
case "leaf": {
|
|
313
|
+
const leaf = this.leafValues[node.index];
|
|
314
|
+
return { neg: leaf.neg, mag: leaf.mag };
|
|
315
|
+
}
|
|
316
|
+
case "number": {
|
|
317
|
+
const number = this.numberValues[node.index];
|
|
318
|
+
return { neg: number.neg, mag: number.mag };
|
|
319
|
+
}
|
|
320
|
+
case "const": {
|
|
321
|
+
const neg = node.value < 0n;
|
|
322
|
+
return { neg: neg ? "true" : "false", mag: i64Const(neg ? -node.value : node.value) };
|
|
323
|
+
}
|
|
324
|
+
case "bin": {
|
|
325
|
+
const a = this.exact(node.left);
|
|
326
|
+
const b = this.exact(node.right);
|
|
327
|
+
switch (node.op) {
|
|
328
|
+
case "+":
|
|
329
|
+
return this.add(a, b);
|
|
330
|
+
case "-":
|
|
331
|
+
return this.add(a, this.negate(b));
|
|
332
|
+
case "*": {
|
|
333
|
+
const mag = this.overflowing("umul", a.mag, b.mag);
|
|
334
|
+
return this.signed(this.tmp((t) => `${t} = xor i1 ${a.neg}, ${b.neg}`), mag);
|
|
335
|
+
}
|
|
336
|
+
case "/":
|
|
337
|
+
case "%": {
|
|
338
|
+
// Truncating division on magnitudes; the remainder takes the
|
|
339
|
+
// dividend's sign. Division by zero throws in the fallback.
|
|
340
|
+
const zero = this.tmp((t) => `${t} = icmp eq i64 ${b.mag}, 0`);
|
|
341
|
+
this.bad.push(zero);
|
|
342
|
+
const safe = this.tmp((t) => `${t} = select i1 ${zero}, i64 1, i64 ${b.mag}`);
|
|
343
|
+
const mag = this.tmp((t) => `${t} = ${node.op === "/" ? "udiv" : "urem"} i64 ${a.mag}, ${safe}`);
|
|
344
|
+
const neg = node.op === "/" ? this.tmp((t) => `${t} = xor i1 ${a.neg}, ${b.neg}`) : a.neg;
|
|
345
|
+
return this.signed(neg, mag);
|
|
346
|
+
}
|
|
347
|
+
default: {
|
|
348
|
+
// Bitwise operators act on two's complement: both operands
|
|
349
|
+
// sign-extend into i128, and the result must again have a
|
|
350
|
+
// magnitude below 2^64.
|
|
351
|
+
const op = LL_BINARY[node.op];
|
|
352
|
+
const wide = (v) => {
|
|
353
|
+
const m = this.tmp((t) => `${t} = zext i64 ${v.mag} to i128`);
|
|
354
|
+
const n = this.tmp((t) => `${t} = sub i128 0, ${m}`);
|
|
355
|
+
return this.tmp((t) => `${t} = select i1 ${v.neg}, i128 ${n}, i128 ${m}`);
|
|
356
|
+
};
|
|
357
|
+
const r = this.tmp((t) => `${t} = ${op} i128 ${wide(a)}, ${wide(b)}`);
|
|
358
|
+
const neg = this.tmp((t) => `${t} = icmp slt i128 ${r}, 0`);
|
|
359
|
+
const flipped = this.tmp((t) => `${t} = sub i128 0, ${r}`);
|
|
360
|
+
const abs = this.tmp((t) => `${t} = select i1 ${neg}, i128 ${flipped}, i128 ${r}`);
|
|
361
|
+
const high = this.tmp((t) => `${t} = lshr i128 ${abs}, 64`);
|
|
362
|
+
this.bad.push(this.tmp((t) => `${t} = icmp ne i128 ${high}, 0`));
|
|
363
|
+
return { neg, mag: this.tmp((t) => `${t} = trunc i128 ${abs} to i64`) };
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
case "shift": {
|
|
368
|
+
const a = this.exact(node.operand);
|
|
369
|
+
const k = node.count;
|
|
370
|
+
if (node.left) {
|
|
371
|
+
const mag = this.tmp((t) => `${t} = shl i64 ${a.mag}, ${k}`);
|
|
372
|
+
const back = this.tmp((t) => `${t} = lshr i64 ${mag}, ${k}`);
|
|
373
|
+
this.bad.push(this.tmp((t) => `${t} = icmp ne i64 ${back}, ${a.mag}`));
|
|
374
|
+
return { neg: a.neg, mag };
|
|
375
|
+
}
|
|
376
|
+
// Right shifts round toward negative infinity: negative values
|
|
377
|
+
// round their magnitude up.
|
|
378
|
+
const down = this.tmp((t) => `${t} = lshr i64 ${a.mag}, ${k}`);
|
|
379
|
+
if (k === 0)
|
|
380
|
+
return a;
|
|
381
|
+
const lost = this.tmp((t) => `${t} = and i64 ${a.mag}, ${i64Const((1n << BigInt(k)) - 1n)}`);
|
|
382
|
+
const any = this.tmp((t) => `${t} = icmp ne i64 ${lost}, 0`);
|
|
383
|
+
const up = this.tmp((t) => `${t} = and i1 ${any}, ${a.neg}`);
|
|
384
|
+
const bump = this.tmp((t) => `${t} = zext i1 ${up} to i64`);
|
|
385
|
+
const mag = this.tmp((t) => `${t} = add i64 ${down}, ${bump}`);
|
|
386
|
+
return this.signed(a.neg, mag);
|
|
387
|
+
}
|
|
388
|
+
case "neg":
|
|
389
|
+
return this.negate(this.exact(node.operand));
|
|
390
|
+
case "not": {
|
|
391
|
+
// ~a == -a - 1
|
|
392
|
+
const a = this.exact(node.operand);
|
|
393
|
+
return this.add(this.negate(a), { neg: "true", mag: "1" });
|
|
394
|
+
}
|
|
395
|
+
case "wrap": {
|
|
396
|
+
// Built in the fixed-width mode: only low bits matter below.
|
|
397
|
+
const low = this.narrow(this.wrap(node.operand), node.signed, node.bits);
|
|
398
|
+
if (!node.signed)
|
|
399
|
+
return { neg: "false", mag: low };
|
|
400
|
+
const neg = this.tmp((t) => `${t} = icmp slt i64 ${low}, 0`);
|
|
401
|
+
const negated = this.tmp((t) => `${t} = sub i64 0, ${low}`);
|
|
402
|
+
return { neg, mag: this.tmp((t) => `${t} = select i1 ${neg}, i64 ${negated}, i64 ${low}`) };
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
/** Below a narrowing, leaves may be this many 64-bit words long: with at
|
|
408
|
+
* most 25 leaves and shifts below 64 bits, no exact intermediate value
|
|
409
|
+
* comes near the size limit (2^24 words), so no operation could throw. */
|
|
410
|
+
const NARROWED_LEAF_WORDS = 65536;
|
|
411
|
+
/** A bigint leaf: two's complement low bits, sign and low 64 bits of the
|
|
412
|
+
* magnitude, whether the magnitude fits in 64 bits (`ok`) and whether it
|
|
413
|
+
* is short enough for a narrowed tree (`short`). 64-bit targets read the
|
|
414
|
+
* value inline ({ rc, len, i32 sign, i32 cap, limbs }; limb 0 is always
|
|
415
|
+
* readable); 32-bit ones ask the runtime. */
|
|
416
|
+
function leafOperand(host, value) {
|
|
417
|
+
const B = host.B;
|
|
418
|
+
const ok = B.tmp();
|
|
419
|
+
const short = B.tmp();
|
|
420
|
+
const neg = B.tmp();
|
|
421
|
+
const mag = B.tmp();
|
|
422
|
+
if (host.sizeType !== "i64") {
|
|
423
|
+
const slot = B.slot();
|
|
424
|
+
const words = B.slot();
|
|
425
|
+
B.entryAllocas.push(`${slot} = alloca i64`);
|
|
426
|
+
B.entryAllocas.push(`${words} = alloca i32`);
|
|
427
|
+
host.declare(`declare i32 @scr_bigint_mag64(ptr, ptr, ptr)`);
|
|
428
|
+
const sign = B.tmp();
|
|
429
|
+
const count = B.tmp();
|
|
430
|
+
B.line(`${sign} = call i32 @scr_bigint_mag64(ptr ${value}, ptr ${slot}, ptr ${words})`);
|
|
431
|
+
B.line(`${mag} = load i64, ptr ${slot}`);
|
|
432
|
+
B.line(`${count} = load i32, ptr ${words}`);
|
|
433
|
+
B.line(`${ok} = icmp ule i32 ${count}, 1`);
|
|
434
|
+
B.line(`${short} = icmp ule i32 ${count}, ${NARROWED_LEAF_WORDS}`);
|
|
435
|
+
B.line(`${neg} = icmp slt i32 ${sign}, 0`);
|
|
436
|
+
}
|
|
437
|
+
else {
|
|
438
|
+
const lenAt = B.tmp();
|
|
439
|
+
const len = B.tmp();
|
|
440
|
+
const signAt = B.tmp();
|
|
441
|
+
const sign = B.tmp();
|
|
442
|
+
const limbAt = B.tmp();
|
|
443
|
+
B.line(`${lenAt} = getelementptr inbounds i8, ptr ${value}, i64 8`);
|
|
444
|
+
B.line(`${len} = load i64, ptr ${lenAt}`);
|
|
445
|
+
B.line(`${signAt} = getelementptr inbounds i8, ptr ${value}, i64 16`);
|
|
446
|
+
B.line(`${sign} = load i32, ptr ${signAt}`);
|
|
447
|
+
B.line(`${limbAt} = getelementptr inbounds i8, ptr ${value}, i64 24`);
|
|
448
|
+
B.line(`${mag} = load i64, ptr ${limbAt}`);
|
|
449
|
+
B.line(`${ok} = icmp ule i64 ${len}, 1`);
|
|
450
|
+
B.line(`${short} = icmp ule i64 ${len}, ${NARROWED_LEAF_WORDS}`);
|
|
451
|
+
B.line(`${neg} = icmp slt i32 ${sign}, 0`);
|
|
452
|
+
}
|
|
453
|
+
const negated = B.tmp();
|
|
454
|
+
const bits = B.tmp();
|
|
455
|
+
B.line(`${negated} = sub i64 0, ${mag}`);
|
|
456
|
+
B.line(`${bits} = select i1 ${neg}, i64 ${negated}, i64 ${mag}`);
|
|
457
|
+
return { bits, neg, mag, ok, short };
|
|
458
|
+
}
|
|
459
|
+
/** A number operand of BigInt(): an integer below 2^64 in magnitude, or
|
|
460
|
+
* the fallback converts it (and throws for non-integers). */
|
|
461
|
+
function numberOperand(host, value) {
|
|
462
|
+
const B = host.B;
|
|
463
|
+
const abs = B.tmp();
|
|
464
|
+
const inRange = B.tmp();
|
|
465
|
+
const safe = B.tmp();
|
|
466
|
+
const mag = B.tmp();
|
|
467
|
+
const back = B.tmp();
|
|
468
|
+
const integral = B.tmp();
|
|
469
|
+
const ok = B.tmp();
|
|
470
|
+
const neg = B.tmp();
|
|
471
|
+
const negated = B.tmp();
|
|
472
|
+
const bits = B.tmp();
|
|
473
|
+
host.declare(`declare double @llvm.fabs.f64(double)`);
|
|
474
|
+
B.line(`${abs} = call double @llvm.fabs.f64(double ${value})`);
|
|
475
|
+
B.line(`${inRange} = fcmp olt double ${abs}, 1.8446744073709552e19`);
|
|
476
|
+
B.line(`${safe} = select i1 ${inRange}, double ${abs}, double 0.0`);
|
|
477
|
+
B.line(`${mag} = fptoui double ${safe} to i64`);
|
|
478
|
+
B.line(`${back} = uitofp i64 ${mag} to double`);
|
|
479
|
+
B.line(`${integral} = fcmp oeq double ${back}, ${abs}`);
|
|
480
|
+
B.line(`${ok} = and i1 ${inRange}, ${integral}`);
|
|
481
|
+
// -0 converts to 0n; a zero magnitude is never negative.
|
|
482
|
+
B.line(`${neg} = fcmp olt double ${value}, 0.0`);
|
|
483
|
+
B.line(`${negated} = sub i64 0, ${mag}`);
|
|
484
|
+
B.line(`${bits} = select i1 ${neg}, i64 ${negated}, i64 ${mag}`);
|
|
485
|
+
return { bits, neg, mag, ok, short: ok };
|
|
486
|
+
}
|
|
487
|
+
/** Emits a small-value bigint tree, or returns null when `e` is not one
|
|
488
|
+
* (the caller then emits the plain library call). */
|
|
489
|
+
export function emitBigIntWrap(host, e) {
|
|
490
|
+
const root = rootOf(e);
|
|
491
|
+
if (root === null)
|
|
492
|
+
return null;
|
|
493
|
+
let tree = new TreeBuilder(root.mode, false);
|
|
494
|
+
let operand = tree.node(root.operand, 0);
|
|
495
|
+
if (tree.ok && tree.pure) {
|
|
496
|
+
tree = new TreeBuilder(root.mode, true);
|
|
497
|
+
operand = tree.node(root.operand, 0);
|
|
498
|
+
}
|
|
499
|
+
// Something to fuse: an operation below a fixed-width root, two in an
|
|
500
|
+
// exact tree.
|
|
501
|
+
if (!tree.ok || tree.operations < (root.mode === "wrap" ? 1 : 2))
|
|
502
|
+
return null;
|
|
503
|
+
const whole = root.wrap ? { kind: "wrap", ...root.wrap, operand } : operand;
|
|
504
|
+
if (root.wrap)
|
|
505
|
+
tree.program.push(`${root.wrap.signed ? "i" : "u"}${root.wrap.bits}`);
|
|
506
|
+
const B = host.B;
|
|
507
|
+
const size = host.sizeType;
|
|
508
|
+
// Operands in evaluation order; literals are immortal statics.
|
|
509
|
+
const values = tree.leaves.map((leaf) => emitBorrowedInput(host, leaf));
|
|
510
|
+
const numbers = tree.numbers.map((n) => host.emitExpr(n));
|
|
511
|
+
const checks = [];
|
|
512
|
+
const leafValues = values.map((v, i) => {
|
|
513
|
+
if (literalValue(tree.leaves[i]) !== null) {
|
|
514
|
+
return { bits: "0", neg: "false", mag: "0", ok: "true", short: "true" };
|
|
515
|
+
}
|
|
516
|
+
const leaf = leafOperand(host, v.name);
|
|
517
|
+
checks.push(tree.strict.has(i) ? leaf.ok : leaf.short);
|
|
518
|
+
return leaf;
|
|
519
|
+
});
|
|
520
|
+
const numberValues = numbers.map((n) => {
|
|
521
|
+
const number = numberOperand(host, n.name);
|
|
522
|
+
checks.push(number.ok);
|
|
523
|
+
return number;
|
|
524
|
+
});
|
|
525
|
+
const emitter = new TreeEmitter(host, leafValues, numberValues);
|
|
526
|
+
const and = (acc, ok) => {
|
|
527
|
+
const t = B.tmp();
|
|
528
|
+
B.line(`${t} = and i1 ${acc}, ${ok}`);
|
|
529
|
+
return t;
|
|
530
|
+
};
|
|
531
|
+
const resultSlot = B.slot();
|
|
532
|
+
B.entryAllocas.push(`${resultSlot} = alloca ptr`);
|
|
533
|
+
const fast = B.newLabel("bigint.wrap.fast");
|
|
534
|
+
const slow = B.newLabel("bigint.wrap.slow");
|
|
535
|
+
const done = B.newLabel("bigint.wrap.done");
|
|
536
|
+
if (root.mode === "exact") {
|
|
537
|
+
// Exact trees decide after computing (overflow), so compute first and
|
|
538
|
+
// branch once. Out-of-range operands only make the work moot.
|
|
539
|
+
const value = emitter.exact(whole);
|
|
540
|
+
const good = emitter.bad.map((b) => {
|
|
541
|
+
const t = B.tmp();
|
|
542
|
+
B.line(`${t} = xor i1 ${b}, true`);
|
|
543
|
+
return t;
|
|
544
|
+
});
|
|
545
|
+
B.condBr([...checks, ...good].reduce(and, "true"), fast, slow);
|
|
546
|
+
B.startBlock(fast);
|
|
547
|
+
host.declare(`declare ptr @scr_bigint_from_mag64(i64, i1 zeroext)`);
|
|
548
|
+
const made = B.tmp();
|
|
549
|
+
B.line(`${made} = call ptr @scr_bigint_from_mag64(i64 ${value.mag}, i1 zeroext ${value.neg})`);
|
|
550
|
+
B.line(`store ptr ${made}, ptr ${resultSlot}`);
|
|
551
|
+
}
|
|
552
|
+
else {
|
|
553
|
+
B.condBr(checks.reduce(and, "true"), fast, slow);
|
|
554
|
+
B.startBlock(fast);
|
|
555
|
+
const low = emitter.wrap(whole);
|
|
556
|
+
const ctor = root.wrap?.signed ? "scr_bigint_from_i64" : "scr_bigint_from_u64";
|
|
557
|
+
host.declare(`declare ptr @${ctor}(i64)`);
|
|
558
|
+
const made = B.tmp();
|
|
559
|
+
B.line(`${made} = call ptr @${ctor}(i64 ${low})`);
|
|
560
|
+
B.line(`store ptr ${made}, ptr ${resultSlot}`);
|
|
561
|
+
}
|
|
562
|
+
B.br(done);
|
|
563
|
+
B.startBlock(slow);
|
|
564
|
+
const leafArray = B.slot();
|
|
565
|
+
B.entryAllocas.push(`${leafArray} = alloca [${Math.max(1, values.length)} x ptr]`);
|
|
566
|
+
for (let i = 0; i < values.length; i++) {
|
|
567
|
+
const at = B.tmp();
|
|
568
|
+
B.line(`${at} = getelementptr inbounds [${values.length} x ptr], ptr ${leafArray}, ${size} 0, ${size} ${i}`);
|
|
569
|
+
B.line(`store ptr ${values[i].name}, ptr ${at}`);
|
|
570
|
+
}
|
|
571
|
+
const numberArray = B.slot();
|
|
572
|
+
B.entryAllocas.push(`${numberArray} = alloca [${Math.max(1, numbers.length)} x double]`);
|
|
573
|
+
for (let i = 0; i < numbers.length; i++) {
|
|
574
|
+
const at = B.tmp();
|
|
575
|
+
B.line(`${at} = getelementptr inbounds [${numbers.length} x double], ptr ${numberArray}, ${size} 0, ${size} ${i}`);
|
|
576
|
+
B.line(`store double ${numbers[i].name}, ptr ${at}`);
|
|
577
|
+
}
|
|
578
|
+
host.declare(`declare ptr @scr_bigint_wrap_eval(ptr, ptr, ptr)`);
|
|
579
|
+
const exact = B.tmp();
|
|
580
|
+
const program = host.cstr(tree.program.join(" "));
|
|
581
|
+
B.line(`${exact} = call ptr @scr_bigint_wrap_eval(ptr ${program}, ptr ${leafArray}, ptr ${numberArray})`);
|
|
582
|
+
B.line(`store ptr ${exact}, ptr ${resultSlot}`);
|
|
583
|
+
host.emitPendingCheck();
|
|
584
|
+
B.br(done);
|
|
585
|
+
B.startBlock(done);
|
|
586
|
+
const result = B.tmp();
|
|
587
|
+
B.line(`${result} = load ptr, ptr ${resultSlot}`);
|
|
588
|
+
return host.own({ name: result, type: e.type });
|
|
589
|
+
}
|
|
590
|
+
//# sourceMappingURL=bigint-wrap.js.map
|