@harmoniclabs/pebble 0.1.10 → 0.3.0
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/dist/IR/CompilationCtx.d.ts +40 -0
- package/dist/IR/CompilationCtx.js +54 -0
- package/dist/IR/IRHash.d.ts +23 -2
- package/dist/IR/IRHash.js +10 -60
- package/dist/IR/IRNodes/IRConst.js +35 -4
- package/dist/IR/IRNodes/IRHoisted.d.ts +0 -1
- package/dist/IR/IRNodes/IRHoisted.js +4 -6
- package/dist/IR/IRNodes/IRLetted.d.ts +0 -1
- package/dist/IR/IRNodes/IRLetted.js +4 -6
- package/dist/IR/IRNodes/IRNative/IRNativeTag.d.ts +22 -2
- package/dist/IR/IRNodes/IRNative/IRNativeTag.js +26 -2
- package/dist/IR/IRNodes/IRNative/index.d.ts +16 -1
- package/dist/IR/IRNodes/IRNative/index.js +31 -2
- package/dist/IR/IRNodes/utils/hashVarSym.d.ts +0 -1
- package/dist/IR/IRNodes/utils/hashVarSym.js +27 -33
- package/dist/IR/toUPLC/CompilerOptions.d.ts +34 -7
- package/dist/IR/toUPLC/CompilerOptions.js +19 -10
- package/dist/IR/toUPLC/compileIRToUPLC.js +39 -3
- package/dist/IR/toUPLC/subRoutines/inlineSingleUseLetBindingsAndReturnRoot.d.ts +23 -0
- package/dist/IR/toUPLC/subRoutines/inlineSingleUseLetBindingsAndReturnRoot.js +263 -0
- package/dist/IR/toUPLC/subRoutines/introduceCaseForDualHeadTailAndReturnRoot.d.ts +35 -0
- package/dist/IR/toUPLC/subRoutines/introduceCaseForDualHeadTailAndReturnRoot.js +169 -0
- package/dist/IR/toUPLC/subRoutines/replaceHoistedWithLetted.d.ts +0 -1
- package/dist/IR/toUPLC/subRoutines/replaceHoistedWithLetted.js +6 -6
- package/dist/IR/toUPLC/subRoutines/replaceNatives/nativeToIR.d.ts +2 -3
- package/dist/IR/toUPLC/subRoutines/replaceNatives/nativeToIR.js +106 -65
- package/dist/IR/toUPLC/subRoutines/rewriteHeadTailInCaseConsAndReturnRoot.d.ts +30 -0
- package/dist/IR/toUPLC/subRoutines/rewriteHeadTailInCaseConsAndReturnRoot.js +95 -0
- package/dist/IR/toUPLC/subRoutines/rewriteNativesAppliedToConstantsAndReturnRoot.js +36 -5
- package/dist/IR/toUPLC/subRoutines/rewriteToCaseOverConstAndReturnRoot.d.ts +35 -0
- package/dist/IR/toUPLC/subRoutines/rewriteToCaseOverConstAndReturnRoot.js +169 -0
- package/dist/IR/tree_utils/_ir_caseList.d.ts +15 -0
- package/dist/IR/tree_utils/_ir_caseList.js +19 -0
- package/dist/IR/tree_utils/bytesToHex.d.ts +8 -0
- package/dist/IR/tree_utils/bytesToHex.js +69 -0
- package/dist/ast/nodes/expr/functions/FuncExpr.d.ts +16 -2
- package/dist/ast/nodes/expr/functions/FuncExpr.js +17 -0
- package/dist/ast/nodes/expr/litteral/LitteralExpr.d.ts +2 -1
- package/dist/ast/nodes/expr/litteral/LitteralExpr.js +2 -0
- package/dist/ast/nodes/expr/litteral/TemplateStrExpr.d.ts +30 -0
- package/dist/ast/nodes/expr/litteral/TemplateStrExpr.js +35 -0
- package/dist/ast/nodes/statements/ExportStmt.d.ts +3 -3
- package/dist/ast/nodes/statements/PebbleStmt.d.ts +4 -3
- package/dist/ast/nodes/statements/PebbleStmt.js +6 -2
- package/dist/ast/nodes/statements/TestParam.d.ts +18 -0
- package/dist/ast/nodes/statements/TestParam.js +18 -0
- package/dist/ast/nodes/statements/TestStmt.d.ts +5 -3
- package/dist/ast/nodes/statements/TestStmt.js +3 -1
- package/dist/ast/nodes/statements/UsingStmt.d.ts +32 -2
- package/dist/ast/nodes/statements/UsingStmt.js +39 -3
- package/dist/ast/nodes/statements/declarations/NamespaceDecl.d.ts +21 -0
- package/dist/ast/nodes/statements/declarations/NamespaceDecl.js +31 -0
- package/dist/ast/nodes/statements/declarations/StructDecl.d.ts +16 -2
- package/dist/ast/nodes/statements/declarations/StructDecl.js +15 -1
- package/dist/compiler/AstCompiler/AstCompiler.d.ts +27 -0
- package/dist/compiler/AstCompiler/AstCompiler.js +244 -7
- package/dist/compiler/AstCompiler/internal/_deriveContractBody/_deriveContractBody.js +16 -5
- package/dist/compiler/AstCompiler/internal/exprs/_compileCallExpr.js +97 -6
- package/dist/compiler/AstCompiler/internal/exprs/_compileCaseExpr.js +31 -0
- package/dist/compiler/AstCompiler/internal/exprs/_compileFuncExpr.js +12 -5
- package/dist/compiler/AstCompiler/internal/exprs/_compileIsExpr.js +12 -0
- package/dist/compiler/AstCompiler/internal/exprs/_compileLitteralExpr.js +59 -0
- package/dist/compiler/AstCompiler/internal/exprs/_compilePropAccessExpr.d.ts +2 -3
- package/dist/compiler/AstCompiler/internal/exprs/_compilePropAccessExpr.js +64 -0
- package/dist/compiler/AstCompiler/internal/exprs/_compileUnaryPrefixExpr.js +13 -1
- package/dist/compiler/AstCompiler/internal/exprs/_compileVarAccessExpr.js +2 -0
- package/dist/compiler/AstCompiler/internal/exprs/binary/_compileAddExpr.js +18 -5
- package/dist/compiler/AstCompiler/internal/exprs/binary/_compileEqualExpr.js +3 -1
- package/dist/compiler/AstCompiler/internal/exprs/binary/_compileGreaterThanEqualExpr.js +2 -1
- package/dist/compiler/AstCompiler/internal/exprs/binary/_compileGreaterThanExpr.js +2 -1
- package/dist/compiler/AstCompiler/internal/exprs/binary/_compileLessThanEqualExpr.js +2 -1
- package/dist/compiler/AstCompiler/internal/exprs/binary/_compileLessThanExpr.js +2 -1
- package/dist/compiler/AstCompiler/internal/exprs/binary/_compileMultExpr.js +24 -6
- package/dist/compiler/AstCompiler/internal/exprs/binary/_compileNotEqualExpr.js +2 -1
- package/dist/compiler/AstCompiler/internal/exprs/binary/_compileSubExpr.js +16 -5
- package/dist/compiler/AstCompiler/internal/statements/_compileMatchStmt.js +33 -20
- package/dist/compiler/AstCompiler/internal/statements/_compileStatement.js +4 -1
- package/dist/compiler/AstCompiler/internal/statements/_compileTestStmt.d.ts +15 -1
- package/dist/compiler/AstCompiler/internal/statements/_compileTestStmt.js +70 -30
- package/dist/compiler/AstCompiler/internal/statements/_compileUsingAliasStmt.d.ts +11 -0
- package/dist/compiler/AstCompiler/internal/statements/_compileUsingAliasStmt.js +26 -0
- package/dist/compiler/AstCompiler/internal/statements/_compileUsingStmt.d.ts +9 -4
- package/dist/compiler/AstCompiler/internal/statements/_compileUsingStmt.js +51 -10
- package/dist/compiler/AstCompiler/internal/types/_compileDataEncodedConcreteType.js +21 -2
- package/dist/compiler/AstCompiler/internal/types/_compileSopEncodedConcreteType.js +17 -2
- package/dist/compiler/AstCompiler/scope/AstScope.d.ts +70 -1
- package/dist/compiler/AstCompiler/scope/AstScope.js +91 -0
- package/dist/compiler/AstCompiler/utils/getPropAccessReturnType.js +36 -1
- package/dist/compiler/AstCompiler/utils/monomorphizeGeneric.d.ts +36 -0
- package/dist/compiler/AstCompiler/utils/monomorphizeGeneric.js +123 -0
- package/dist/compiler/AstCompiler/utils/resolveNamespaceChain.d.ts +28 -0
- package/dist/compiler/AstCompiler/utils/resolveNamespaceChain.js +95 -0
- package/dist/compiler/AstCompiler/utils/resolveNamespacePath.d.ts +37 -0
- package/dist/compiler/AstCompiler/utils/resolveNamespacePath.js +93 -0
- package/dist/compiler/Compiler.d.ts +9 -1
- package/dist/compiler/Compiler.js +218 -30
- package/dist/compiler/TirCompiler/expressify/ExpressifyCtx.js +1 -1
- package/dist/compiler/TirCompiler/expressify/expressify.js +30 -2
- package/dist/compiler/TirCompiler/expressify/expressifyForStmt.d.ts +2 -1
- package/dist/compiler/TirCompiler/expressify/expressifyForStmt.js +45 -7
- package/dist/compiler/TirCompiler/expressify/expressifyVars.d.ts +0 -1
- package/dist/compiler/TirCompiler/expressify/expressifyVars.js +49 -15
- package/dist/compiler/test/TestResult.d.ts +38 -0
- package/dist/compiler/test/TestResult.js +6 -0
- package/dist/compiler/test/fuzz/PRNG.d.ts +26 -0
- package/dist/compiler/test/fuzz/PRNG.js +59 -0
- package/dist/compiler/tir/expressions/TirCaseExpr.d.ts +9 -0
- package/dist/compiler/tir/expressions/TirCaseExpr.js +144 -122
- package/dist/compiler/tir/expressions/TirElemAccessExpr.js +2 -2
- package/dist/compiler/tir/expressions/TirExpr.d.ts +2 -1
- package/dist/compiler/tir/expressions/TirExpr.js +2 -0
- package/dist/compiler/tir/expressions/TirFromDataExpr.js +102 -67
- package/dist/compiler/tir/expressions/TirIsExpr.js +14 -1
- package/dist/compiler/tir/expressions/TirNativeFunc.d.ts +18 -2
- package/dist/compiler/tir/expressions/TirNativeFunc.js +55 -118
- package/dist/compiler/tir/expressions/TirShowExpr.d.ts +52 -0
- package/dist/compiler/tir/expressions/TirShowExpr.js +199 -0
- package/dist/compiler/tir/expressions/TirToDataExpr.js +3 -0
- package/dist/compiler/tir/expressions/TirTraceExpr.js +11 -7
- package/dist/compiler/tir/expressions/TirTypeConversionExpr.js +10 -0
- package/dist/compiler/tir/expressions/TirVariableAccessExpr.d.ts +2 -3
- package/dist/compiler/tir/expressions/TirVariableAccessExpr.js +1 -4
- package/dist/compiler/tir/expressions/ToIRTermCtx.d.ts +20 -3
- package/dist/compiler/tir/expressions/ToIRTermCtx.js +48 -3
- package/dist/compiler/tir/expressions/binary/TirBinaryExpr.d.ts +2 -2
- package/dist/compiler/tir/expressions/binary/TirBinaryExpr.js +45 -8
- package/dist/compiler/tir/expressions/litteral/TirLitEnumMemberExpr.d.ts +19 -0
- package/dist/compiler/tir/expressions/litteral/TirLitEnumMemberExpr.js +24 -0
- package/dist/compiler/tir/expressions/litteral/TirLitteralExpr.d.ts +2 -1
- package/dist/compiler/tir/expressions/litteral/TirLitteralExpr.js +2 -0
- package/dist/compiler/tir/expressions/unary/TirUnaryMinus.js +4 -1
- package/dist/compiler/tir/program/TypedProgram.d.ts +101 -0
- package/dist/compiler/tir/program/TypedProgram.js +43 -0
- package/dist/compiler/tir/program/stdScope/populateBuiltinInterfaces.d.ts +17 -0
- package/dist/compiler/tir/program/stdScope/populateBuiltinInterfaces.js +70 -0
- package/dist/compiler/tir/program/stdScope/populateStdNamespace.d.ts +22 -0
- package/dist/compiler/tir/program/stdScope/populateStdNamespace.js +619 -0
- package/dist/compiler/tir/program/stdScope/prelude/preludeTypesSrc.js +35 -2
- package/dist/compiler/tir/program/stdScope/stdScope.d.ts +8 -0
- package/dist/compiler/tir/program/stdScope/stdScope.js +84 -41
- package/dist/compiler/tir/statements/TirStmt.js +0 -1
- package/dist/compiler/tir/statements/TirTestStmt.d.ts +46 -0
- package/dist/compiler/tir/statements/TirTestStmt.js +35 -0
- package/dist/compiler/tir/types/TirEnumType.d.ts +21 -0
- package/dist/compiler/tir/types/TirEnumType.js +36 -0
- package/dist/compiler/tir/types/TirNativeType/TirNativeType.d.ts +53 -2
- package/dist/compiler/tir/types/TirNativeType/TirNativeType.js +58 -1
- package/dist/compiler/tir/types/TirNativeType/native/array.d.ts +16 -0
- package/dist/compiler/tir/types/TirNativeType/native/array.js +38 -0
- package/dist/compiler/tir/types/TirNativeType/native/index.d.ts +2 -0
- package/dist/compiler/tir/types/TirNativeType/native/index.js +2 -0
- package/dist/compiler/tir/types/TirNativeType/native/value.d.ts +18 -0
- package/dist/compiler/tir/types/TirNativeType/native/value.js +17 -0
- package/dist/compiler/tir/types/TirStructType.js +6 -1
- package/dist/compiler/tir/types/TirType.d.ts +3 -2
- package/dist/compiler/tir/types/TirType.js +7 -2
- package/dist/compiler/tir/types/utils/canAssignTo.js +36 -1
- package/dist/compiler/tir/types/utils/canCastTo.js +14 -1
- package/dist/compiler/tir/types/utils/getDeconstructableType.d.ts +2 -1
- package/dist/compiler/tir/types/utils/getDeconstructableType.js +2 -0
- package/dist/compiler/tir/types/utils/inferTypeArgs.d.ts +19 -0
- package/dist/compiler/tir/types/utils/inferTypeArgs.js +83 -0
- package/dist/compiler/tir/types/utils/normalizeEnumToInt.d.ts +10 -0
- package/dist/compiler/tir/types/utils/normalizeEnumToInt.js +17 -0
- package/dist/compiler/tir/types/utils/substituteTypeParams.d.ts +9 -0
- package/dist/compiler/tir/types/utils/substituteTypeParams.js +67 -0
- package/dist/diagnostics/diagnosticMessages.generated.d.ts +10 -0
- package/dist/diagnostics/diagnosticMessages.generated.js +20 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/parser/Parser.d.ts +73 -3
- package/dist/parser/Parser.js +362 -46
- package/dist/tokenizer/Token.d.ts +106 -102
- package/dist/tokenizer/Token.js +111 -109
- package/dist/tokenizer/utils/tokenFromKeyword.js +11 -6
- package/dist/utils/semverSatisfies.d.ts +1 -0
- package/dist/utils/semverSatisfies.js +161 -0
- package/dist/version.generated.d.ts +1 -0
- package/dist/version.generated.js +2 -0
- package/package.json +5 -4
- package/dist/IR/tree_utils/_ir_lazyChooseList.d.ts +0 -3
- package/dist/IR/tree_utils/_ir_lazyChooseList.js +0 -7
|
@@ -1,36 +1,30 @@
|
|
|
1
|
-
// HASH GENERATOR
|
|
2
1
|
import { fromHex } from "@harmoniclabs/uint8array-utils";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
function
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Stable per-symbol bytes used as the preimage contribution of an
|
|
4
|
+
* `IRVar` / self-reference when hashing.
|
|
5
|
+
*
|
|
6
|
+
* Each distinct symbol is assigned a unique 8-byte id the first time it
|
|
7
|
+
* is hashed; subsequent calls for the same symbol return the same bytes.
|
|
8
|
+
* This makes hashing a pure function of the (symbol-identity) content,
|
|
9
|
+
* so a term's hash is stable for the life of the process — exactly what
|
|
10
|
+
* content-addressed `IRHash` requires.
|
|
11
|
+
*
|
|
12
|
+
* No global state needs resetting:
|
|
13
|
+
* - the map is a `WeakMap`, so a symbol's entry is collected once the
|
|
14
|
+
* symbol itself is unreachable (no unbounded growth);
|
|
15
|
+
* - the counter is monotonic and never rewinds, so a symbol can never
|
|
16
|
+
* be reassigned a colliding id (the bug that the old reset-based
|
|
17
|
+
* scheme introduced).
|
|
18
|
+
*/
|
|
19
|
+
// `any` key: non-registered symbols are valid WeakMap keys at runtime
|
|
20
|
+
// (ES2023 / Node 20+), but older `lib` typings don't model symbol keys.
|
|
21
|
+
const _sym_to_bytes = new WeakMap();
|
|
22
|
+
let _next_id = 0n;
|
|
22
23
|
export function hashVarSym(s) {
|
|
23
|
-
|
|
24
|
-
if (
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
throw new Error("ran out of IR hashes");
|
|
30
|
-
const result_hash = unusedHashes.shift() ?? _next_hash++;
|
|
31
|
-
_sym_to_hash.set(s, result_hash);
|
|
32
|
-
_hash_to_sym.set(result_hash, s);
|
|
33
|
-
return fromHex((BigInt(result_hash) + BigInt(MAX_SAFE_INTEGER))
|
|
34
|
-
.toString(16)
|
|
35
|
-
.padStart(16, "0"));
|
|
24
|
+
let bytes = _sym_to_bytes.get(s);
|
|
25
|
+
if (bytes !== undefined)
|
|
26
|
+
return bytes;
|
|
27
|
+
bytes = fromHex((_next_id++).toString(16).padStart(16, "0"));
|
|
28
|
+
_sym_to_bytes.set(s, bytes);
|
|
29
|
+
return bytes;
|
|
36
30
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { UPLCVersion } from "@harmoniclabs/uplc";
|
|
2
|
+
import { COMPILER_VERSION } from "../../version.generated.js";
|
|
3
|
+
export { COMPILER_VERSION };
|
|
2
4
|
export interface CompilerUplcOptimizations {
|
|
3
5
|
/**
|
|
4
6
|
*
|
|
@@ -21,6 +23,13 @@ export declare const defaultUplcOptimizations: CompilerUplcOptimizations;
|
|
|
21
23
|
export declare function isDebugUplcOptimizations(options?: Partial<CompilerUplcOptimizations>): boolean;
|
|
22
24
|
export declare function completeUplcOptimizations(options: Partial<CompilerUplcOptimizations>, complete?: CompilerUplcOptimizations): CompilerUplcOptimizations;
|
|
23
25
|
export interface CompilerOptions {
|
|
26
|
+
/**
|
|
27
|
+
* npm-style semver range that the running compiler version must satisfy.
|
|
28
|
+
* Required since `@harmoniclabs/pebble@0.2.0` — the `Compiler` throws
|
|
29
|
+
* when this field is missing or when the running compiler version does
|
|
30
|
+
* not satisfy the range.
|
|
31
|
+
*/
|
|
32
|
+
compilerVersion: string;
|
|
24
33
|
/**
|
|
25
34
|
* path to the entry file
|
|
26
35
|
*/
|
|
@@ -75,11 +84,29 @@ export interface CompilerOptions {
|
|
|
75
84
|
*
|
|
76
85
|
**/
|
|
77
86
|
addMarker: boolean;
|
|
87
|
+
/**
|
|
88
|
+
* Controls the default Data encoding for single-constructor shortcut
|
|
89
|
+
* struct declarations (`struct Foo { x: int }`).
|
|
90
|
+
*
|
|
91
|
+
* - `"default"`: shortcut single-constructor structs are encoded as
|
|
92
|
+
* `constrData(0, fields)` (backwards-compatible). Use the explicit
|
|
93
|
+
* `untagged` keyword to opt a specific struct in to the minimal form.
|
|
94
|
+
* - `"minimal"`: shortcut single-constructor structs are encoded as
|
|
95
|
+
* `listData(fields)` (smaller / cheaper). The explicit `untagged`
|
|
96
|
+
* keyword still forces minimal regardless.
|
|
97
|
+
*/
|
|
98
|
+
encodingStrategy: "default" | "minimal";
|
|
78
99
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
export
|
|
85
|
-
export declare
|
|
100
|
+
/**
|
|
101
|
+
* Option templates intentionally omit `compilerVersion` — every caller must
|
|
102
|
+
* set it explicitly so a missing `compilerVersion` always surfaces as an
|
|
103
|
+
* error from the `Compiler` constructor.
|
|
104
|
+
*/
|
|
105
|
+
export type CompilerDefaults = Omit<CompilerOptions, "compilerVersion">;
|
|
106
|
+
export declare const extremeOptions: CompilerDefaults;
|
|
107
|
+
export declare const productionOptions: CompilerDefaults;
|
|
108
|
+
export declare const debugOptions: CompilerDefaults;
|
|
109
|
+
export declare const defaultOptions: CompilerDefaults;
|
|
110
|
+
export declare const testOptions: CompilerDefaults;
|
|
111
|
+
export declare const defulatCompilerOptions: CompilerDefaults;
|
|
112
|
+
export declare function completeCompilerOptions(options: Partial<CompilerOptions>, complete?: Partial<CompilerOptions>): CompilerOptions;
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { isObject } from "@harmoniclabs/obj-utils";
|
|
2
|
-
import {
|
|
2
|
+
import { UPLCVersion } from "@harmoniclabs/uplc";
|
|
3
|
+
import { COMPILER_VERSION } from "../../version.generated.js";
|
|
4
|
+
const defaultTargetUplcVersion = new UPLCVersion(1, 1, 0);
|
|
5
|
+
export { COMPILER_VERSION };
|
|
3
6
|
export const productionUplcOptimizations = Object.freeze({
|
|
4
7
|
groupApplications: true,
|
|
5
8
|
inlineSingleUse: true,
|
|
@@ -37,33 +40,36 @@ export const extremeOptions = Object.freeze({
|
|
|
37
40
|
root: ".",
|
|
38
41
|
outDir: "./out",
|
|
39
42
|
silent: false,
|
|
40
|
-
targetUplcVersion:
|
|
43
|
+
targetUplcVersion: defaultTargetUplcVersion,
|
|
41
44
|
removeTraces: true,
|
|
42
45
|
delayHoists: true,
|
|
43
46
|
uplcOptimizations: productionUplcOptimizations,
|
|
44
|
-
addMarker: true
|
|
47
|
+
addMarker: true,
|
|
48
|
+
encodingStrategy: "minimal"
|
|
45
49
|
});
|
|
46
50
|
export const productionOptions = Object.freeze({
|
|
47
51
|
entry: "./src/index.pebble",
|
|
48
52
|
root: ".",
|
|
49
53
|
outDir: "./out",
|
|
50
54
|
silent: false,
|
|
51
|
-
targetUplcVersion:
|
|
55
|
+
targetUplcVersion: defaultTargetUplcVersion,
|
|
52
56
|
removeTraces: true,
|
|
53
57
|
delayHoists: true,
|
|
54
58
|
uplcOptimizations: productionUplcOptimizations,
|
|
55
|
-
addMarker: true
|
|
59
|
+
addMarker: true,
|
|
60
|
+
encodingStrategy: "default"
|
|
56
61
|
});
|
|
57
62
|
export const debugOptions = Object.freeze({
|
|
58
63
|
entry: "./src/index.pebble",
|
|
59
64
|
root: ".",
|
|
60
65
|
outDir: "./out",
|
|
61
66
|
silent: false,
|
|
62
|
-
targetUplcVersion:
|
|
67
|
+
targetUplcVersion: defaultTargetUplcVersion,
|
|
63
68
|
removeTraces: false,
|
|
64
69
|
delayHoists: false,
|
|
65
70
|
uplcOptimizations: debugUplcOptimizations,
|
|
66
|
-
addMarker: false
|
|
71
|
+
addMarker: false,
|
|
72
|
+
encodingStrategy: "default"
|
|
67
73
|
});
|
|
68
74
|
export const defaultOptions = Object.freeze({
|
|
69
75
|
...productionOptions,
|
|
@@ -75,7 +81,7 @@ export const testOptions = Object.freeze({
|
|
|
75
81
|
});
|
|
76
82
|
export const defulatCompilerOptions = defaultOptions;
|
|
77
83
|
export function completeCompilerOptions(options, complete = defaultOptions) {
|
|
78
|
-
let targetUplcVersion = options.targetUplcVersion instanceof UPLCVersion ? complete.targetUplcVersion :
|
|
84
|
+
let targetUplcVersion = options.targetUplcVersion instanceof UPLCVersion ? complete.targetUplcVersion : defaultTargetUplcVersion;
|
|
79
85
|
complete = {
|
|
80
86
|
...defaultOptions,
|
|
81
87
|
...complete
|
|
@@ -99,10 +105,13 @@ export function completeCompilerOptions(options, complete = defaultOptions) {
|
|
|
99
105
|
// console.log( "completeUplcOptimizations( uplcOptimizations )",completeUplcOptimizations( uplcOptimizations ))
|
|
100
106
|
return {
|
|
101
107
|
...complete,
|
|
102
|
-
|
|
108
|
+
// forward compilerVersion verbatim — no fallback; Compiler throws if missing/invalid
|
|
109
|
+
compilerVersion: options.compilerVersion ?? complete.compilerVersion,
|
|
110
|
+
targetUplcVersion: targetUplcVersion,
|
|
103
111
|
removeTraces: options.removeTraces ?? complete.removeTraces,
|
|
104
112
|
delayHoists: options.delayHoists ?? complete.delayHoists,
|
|
105
113
|
uplcOptimizations: completeUplcOptimizations(uplcOptimizations),
|
|
106
|
-
addMarker: options.addMarker ?? complete.addMarker
|
|
114
|
+
addMarker: options.addMarker ?? complete.addMarker,
|
|
115
|
+
encodingStrategy: options.encodingStrategy ?? complete.encodingStrategy ?? "default"
|
|
107
116
|
};
|
|
108
117
|
}
|
|
@@ -16,6 +16,10 @@ import { IRCase, IRConstr, IRFunc, IRNative } from "../IRNodes/index.js";
|
|
|
16
16
|
import { replaceForcedNativesWithHoisted } from "./subRoutines/replaceForcedNativesWithHoisted.js";
|
|
17
17
|
import { performUplcOptimizationsAndReturnRoot } from "./subRoutines/performUplcOptimizationsAndReturnRoot/performUplcOptimizationsAndReturnRoot.js";
|
|
18
18
|
import { rewriteNativesAppliedToConstantsAndReturnRoot } from "./subRoutines/rewriteNativesAppliedToConstantsAndReturnRoot.js";
|
|
19
|
+
import { rewriteToCaseOverConstAndReturnRoot } from "./subRoutines/rewriteToCaseOverConstAndReturnRoot.js";
|
|
20
|
+
import { rewriteHeadTailInCaseConsAndReturnRoot } from "./subRoutines/rewriteHeadTailInCaseConsAndReturnRoot.js";
|
|
21
|
+
import { introduceCaseForDualHeadTailAndReturnRoot } from "./subRoutines/introduceCaseForDualHeadTailAndReturnRoot.js";
|
|
22
|
+
import { inlineSingleUseLetBindingsAndReturnRoot } from "./subRoutines/inlineSingleUseLetBindingsAndReturnRoot.js";
|
|
19
23
|
import { _debug_assertClosedIR } from "../utils/index.js";
|
|
20
24
|
import { ToUplcCtx } from "./ctx/ToUplcCtx.js";
|
|
21
25
|
import { removeUnusedVarsAndReturnRoot } from "./subRoutines/removeUnusuedVarsAndReturnRoot/removeUnusuedVarsAndReturnRoot.js";
|
|
@@ -57,6 +61,29 @@ export function compileIRToUPLC(term, paritalOptions = defaultOptions) {
|
|
|
57
61
|
term = replaceNativesAndReturnRoot(term);
|
|
58
62
|
// re-call rewrite to optimize introduced hoisted
|
|
59
63
|
term = rewriteNativesAppliedToConstantsAndReturnRoot(term);
|
|
64
|
+
// Lower `strictIfThenElse` triple-apps to `IRCase` BEFORE
|
|
65
|
+
// `replaceForcedNativesWithHoisted` would otherwise hoist
|
|
66
|
+
// `(force ifThenElse)` into a shared variable that's no longer
|
|
67
|
+
// pattern-matchable as a native. (`strictChooseList` is already
|
|
68
|
+
// lowered to `IRCase` unconditionally by the earlier
|
|
69
|
+
// `rewriteNativesAppliedToConstantsAndReturnRoot` pass.)
|
|
70
|
+
term = rewriteToCaseOverConstAndReturnRoot(term);
|
|
71
|
+
// Inside `case L of cons h t -> body`, replace any `headList(L)` /
|
|
72
|
+
// `tailList(L)` calls within `body` with `h` / `t`. Drop the now-dead
|
|
73
|
+
// `h`/`t` bindings via a fresh unused-vars sweep.
|
|
74
|
+
term = rewriteHeadTailInCaseConsAndReturnRoot(term);
|
|
75
|
+
term = removeUnusedVarsAndReturnRoot(term);
|
|
76
|
+
// For every IRFunc body where the same list L is accessed via BOTH
|
|
77
|
+
// `headList(L)` and `tailList(L)`, wrap the body in
|
|
78
|
+
// `case L of cons h t -> body' | nil -> error` and substitute the two
|
|
79
|
+
// builtin calls with `h` / `t`. Empirically (bench.headTailVsCase):
|
|
80
|
+
// one case dispatch costs ~128K CPU vs ~160K for two builtin calls,
|
|
81
|
+
// and only ~9 bytes vs ~16. The previous head/tail-in-case-cons pass
|
|
82
|
+
// can then make a second sweep to substitute any further internal
|
|
83
|
+
// references the new case introduced.
|
|
84
|
+
term = introduceCaseForDualHeadTailAndReturnRoot(term);
|
|
85
|
+
term = rewriteHeadTailInCaseConsAndReturnRoot(term);
|
|
86
|
+
term = removeUnusedVarsAndReturnRoot(term);
|
|
60
87
|
// debugAsserts && _debug_assertions( term );
|
|
61
88
|
// unwrap top level letted and hoisted;
|
|
62
89
|
// some natives may be converted to hoisted;
|
|
@@ -144,12 +171,21 @@ export function compileIRToUPLC(term, paritalOptions = defaultOptions) {
|
|
|
144
171
|
// somethingWasInlined = inlineResult.somethingWasInlined;
|
|
145
172
|
// }
|
|
146
173
|
term = removeUnusedVarsAndReturnRoot(term);
|
|
174
|
+
// After `handleLettedAndReturnRoot` lowers `IRLetted` into the
|
|
175
|
+
// `IRApp(IRFunc([p], body), value)` shape, this is the first point
|
|
176
|
+
// where the let-as-application pattern is syntactically visible.
|
|
177
|
+
// Run the inliner here (NOT earlier, where lets are still IRLetted
|
|
178
|
+
// nodes the inliner doesn't recognize). Single-use uses trapped
|
|
179
|
+
// inside nested closures are skipped — see the pass for details.
|
|
180
|
+
term = inlineSingleUseLetBindingsAndReturnRoot(term);
|
|
181
|
+
term = removeUnusedVarsAndReturnRoot(term);
|
|
147
182
|
term = performUplcOptimizationsAndReturnRoot(term, options);
|
|
183
|
+
// Rewrite strictIfThenElse into IRCase-over-Const, and prune
|
|
184
|
+
// trailing IRError continuations from any IRCase.
|
|
185
|
+
term = rewriteToCaseOverConstAndReturnRoot(term);
|
|
148
186
|
term = ensureProperlyForcedBuiltinsAndReturnRoot(term);
|
|
149
187
|
if (options.addMarker &&
|
|
150
|
-
options.targetUplcVersion.
|
|
151
|
-
options.targetUplcVersion.minor >= 1 &&
|
|
152
|
-
options.targetUplcVersion.patch >= 0) {
|
|
188
|
+
options.targetUplcVersion.isV3Friendly()) {
|
|
153
189
|
term = new IRCase(new IRConstr(0, []), [
|
|
154
190
|
term,
|
|
155
191
|
// never evaluated
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inline single-use let-bindings of the shape `((λp. body) value)`:
|
|
3
|
+
*
|
|
4
|
+
* - If `body` references `p` exactly once AND that reference is NOT
|
|
5
|
+
* trapped inside a nested closure (IRFunc/IRRecursive) within
|
|
6
|
+
* `body`, replace the entire app with `body[p := value]`. The
|
|
7
|
+
* nested-closure guard is critical: a use that's syntactically
|
|
8
|
+
* "once" inside a recursive body is actually evaluated once per
|
|
9
|
+
* iteration, and inlining there would move per-iteration work to a
|
|
10
|
+
* value the caller currently evaluates once.
|
|
11
|
+
* - If `body` references `p` zero times, drop the let and replace
|
|
12
|
+
* the app with `body`.
|
|
13
|
+
*
|
|
14
|
+
* IRCase continuations are themselves IRFunc nodes (case-cons branches
|
|
15
|
+
* take h/t lambdas), but the case dispatches each continuation AT MOST
|
|
16
|
+
* ONCE per case eval, so they don't count as "trapping" closures — we
|
|
17
|
+
* treat IRFunc reached *as a direct case continuation* as transparent.
|
|
18
|
+
*
|
|
19
|
+
* If `body` uses `p` two or more times we don't inline either (would
|
|
20
|
+
* duplicate the term in the output).
|
|
21
|
+
*/
|
|
22
|
+
import { IRTerm } from "../../IRTerm.js";
|
|
23
|
+
export declare function inlineSingleUseLetBindingsAndReturnRoot(term: IRTerm): IRTerm;
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inline single-use let-bindings of the shape `((λp. body) value)`:
|
|
3
|
+
*
|
|
4
|
+
* - If `body` references `p` exactly once AND that reference is NOT
|
|
5
|
+
* trapped inside a nested closure (IRFunc/IRRecursive) within
|
|
6
|
+
* `body`, replace the entire app with `body[p := value]`. The
|
|
7
|
+
* nested-closure guard is critical: a use that's syntactically
|
|
8
|
+
* "once" inside a recursive body is actually evaluated once per
|
|
9
|
+
* iteration, and inlining there would move per-iteration work to a
|
|
10
|
+
* value the caller currently evaluates once.
|
|
11
|
+
* - If `body` references `p` zero times, drop the let and replace
|
|
12
|
+
* the app with `body`.
|
|
13
|
+
*
|
|
14
|
+
* IRCase continuations are themselves IRFunc nodes (case-cons branches
|
|
15
|
+
* take h/t lambdas), but the case dispatches each continuation AT MOST
|
|
16
|
+
* ONCE per case eval, so they don't count as "trapping" closures — we
|
|
17
|
+
* treat IRFunc reached *as a direct case continuation* as transparent.
|
|
18
|
+
*
|
|
19
|
+
* If `body` uses `p` two or more times we don't inline either (would
|
|
20
|
+
* duplicate the term in the output).
|
|
21
|
+
*/
|
|
22
|
+
import { IRApp } from "../../IRNodes/IRApp.js";
|
|
23
|
+
import { IRCase } from "../../IRNodes/IRCase.js";
|
|
24
|
+
import { IRConst } from "../../IRNodes/IRConst.js";
|
|
25
|
+
import { IRDelayed } from "../../IRNodes/IRDelayed.js";
|
|
26
|
+
import { IRFunc } from "../../IRNodes/IRFunc.js";
|
|
27
|
+
import { IRHoisted } from "../../IRNodes/IRHoisted.js";
|
|
28
|
+
import { IRLetted } from "../../IRNodes/IRLetted.js";
|
|
29
|
+
import { IRRecursive } from "../../IRNodes/IRRecursive.js";
|
|
30
|
+
import { IRVar } from "../../IRNodes/IRVar.js";
|
|
31
|
+
import { _modifyChildFromTo } from "../_internal/_modifyChildFromTo.js";
|
|
32
|
+
export function inlineSingleUseLetBindingsAndReturnRoot(term) {
|
|
33
|
+
// Iterate until a full pass finds nothing to rewrite. Each rewrite
|
|
34
|
+
// restarts the walk from the (possibly new) root because the tree
|
|
35
|
+
// shape near the rewrite changes and may expose further candidates.
|
|
36
|
+
let didChange = true;
|
|
37
|
+
while (didChange) {
|
|
38
|
+
didChange = false;
|
|
39
|
+
const stack = [term];
|
|
40
|
+
outer: while (stack.length > 0) {
|
|
41
|
+
const t = stack.pop();
|
|
42
|
+
if (t instanceof IRApp
|
|
43
|
+
&& t.fn instanceof IRFunc
|
|
44
|
+
&& t.fn.params.length === 1) {
|
|
45
|
+
const fn = t.fn;
|
|
46
|
+
const p = fn.params[0];
|
|
47
|
+
const value = t.arg;
|
|
48
|
+
// Beta-reduce when the argument is itself just a
|
|
49
|
+
// variable access. Replacing every `IRVar(p)` with
|
|
50
|
+
// `IRVar(x)` costs the same per access (a single env
|
|
51
|
+
// lookup either way) and saves the surrounding
|
|
52
|
+
// `(λp. …) x` lambda + application. Always safe,
|
|
53
|
+
// regardless of how many times `p` is used and
|
|
54
|
+
// regardless of whether the uses sit inside a recursive
|
|
55
|
+
// body — there's no work to multiply.
|
|
56
|
+
if (value instanceof IRVar) {
|
|
57
|
+
const newBody = substituteAllVar(fn.body, p, value);
|
|
58
|
+
term = replaceWithBody(term, t, newBody);
|
|
59
|
+
didChange = true;
|
|
60
|
+
break outer;
|
|
61
|
+
}
|
|
62
|
+
const stats = countVarUses(fn.body, p);
|
|
63
|
+
if (stats.count === 0) {
|
|
64
|
+
// Dead let — replace with body, dropping the value.
|
|
65
|
+
term = replaceWithBody(term, t, fn.body);
|
|
66
|
+
didChange = true;
|
|
67
|
+
break outer;
|
|
68
|
+
}
|
|
69
|
+
if (stats.count === 1 && !stats.trapped) {
|
|
70
|
+
substituteVar(fn.body, p, value);
|
|
71
|
+
term = replaceWithBody(term, t, fn.body);
|
|
72
|
+
didChange = true;
|
|
73
|
+
break outer;
|
|
74
|
+
}
|
|
75
|
+
// Trapped single use: the lone occurrence sits inside a
|
|
76
|
+
// nested closure (recursive body or non-case lambda).
|
|
77
|
+
// Inlining a COMPUTATION there would duplicate the
|
|
78
|
+
// per-iteration work, but a syntactic VALUE (closure /
|
|
79
|
+
// constant / var / delay) only pays its (essentially
|
|
80
|
+
// zero) construction cost — the same as a fresh lookup.
|
|
81
|
+
// Pebble also stores recursive helpers as open-recursion
|
|
82
|
+
// lambdas whose body references their own binding name;
|
|
83
|
+
// inlining such a lambda would orphan that self-ref, so
|
|
84
|
+
// also require that the value has no free `IRVar(p)`.
|
|
85
|
+
if (stats.count === 1
|
|
86
|
+
&& stats.trapped
|
|
87
|
+
&& isSyntacticValue(value)
|
|
88
|
+
&& !containsFreeVar(value, p)) {
|
|
89
|
+
substituteVar(fn.body, p, value);
|
|
90
|
+
term = replaceWithBody(term, t, fn.body);
|
|
91
|
+
didChange = true;
|
|
92
|
+
break outer;
|
|
93
|
+
}
|
|
94
|
+
// count >= 2, or trapped-with-computational-value → leave alone
|
|
95
|
+
}
|
|
96
|
+
stack.push(...t.children());
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return term;
|
|
100
|
+
}
|
|
101
|
+
/** Returns true if `term` contains a free `IRVar(p)` reference. A
|
|
102
|
+
* binding inside `term` that re-uses `p`'s symbol shadows it (these
|
|
103
|
+
* are rare given fresh-symbol minting, but the check is cheap). */
|
|
104
|
+
function containsFreeVar(term, p) {
|
|
105
|
+
const stack = [term];
|
|
106
|
+
while (stack.length > 0) {
|
|
107
|
+
const t = stack.pop();
|
|
108
|
+
if (t instanceof IRVar && t.name === p)
|
|
109
|
+
return true;
|
|
110
|
+
if (t instanceof IRFunc && t.params.includes(p))
|
|
111
|
+
continue;
|
|
112
|
+
if (t instanceof IRRecursive && t.name === p)
|
|
113
|
+
continue;
|
|
114
|
+
stack.push(...t.children());
|
|
115
|
+
}
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* A "syntactic value" is a term whose evaluation does no work beyond
|
|
120
|
+
* binding/closure construction — duplicating it across a recursive
|
|
121
|
+
* boundary doesn't multiply runtime cost. Specifically:
|
|
122
|
+
*
|
|
123
|
+
* - `IRFunc` / `IRRecursive` — closure values; per-construction is
|
|
124
|
+
* ~zero cost, and the body only runs when the closure is applied
|
|
125
|
+
* (which happens at the use-site frequency either way).
|
|
126
|
+
* - `IRConst` — literal value.
|
|
127
|
+
* - `IRVar` — already just an environment lookup.
|
|
128
|
+
* - `IRDelayed` — produces a thunk; the inner term only runs on
|
|
129
|
+
* force, at the use-site frequency.
|
|
130
|
+
*
|
|
131
|
+
* NOTE: `IRNative` is deliberately NOT included. While a bare native
|
|
132
|
+
* reference is itself cheap, in Pebble's pipeline `hoistForcedNatives`
|
|
133
|
+
* specifically wraps each forced builtin (e.g. `headList`,
|
|
134
|
+
* `tailList`, `ifThenElse`) once at the top via `(λvar. …) IRNative`,
|
|
135
|
+
* so that the runtime `force` happens once and is shared. Inlining
|
|
136
|
+
* the `IRNative` back into a recursive body undoes that sharing —
|
|
137
|
+
* the resulting compiled UPLC re-issues the force per iteration.
|
|
138
|
+
* `IRHoisted`/`IRLetted` are transparent wrappers: we unwrap to check
|
|
139
|
+
* the inner term.
|
|
140
|
+
*/
|
|
141
|
+
function isSyntacticValue(t) {
|
|
142
|
+
while (t instanceof IRHoisted)
|
|
143
|
+
t = t.hoisted;
|
|
144
|
+
while (t instanceof IRLetted)
|
|
145
|
+
t = t.value;
|
|
146
|
+
return (t instanceof IRFunc
|
|
147
|
+
|| t instanceof IRRecursive
|
|
148
|
+
|| t instanceof IRConst
|
|
149
|
+
|| t instanceof IRVar
|
|
150
|
+
|| t instanceof IRDelayed);
|
|
151
|
+
}
|
|
152
|
+
function replaceWithBody(root, app, body) {
|
|
153
|
+
const parent = app.parent;
|
|
154
|
+
if (parent === undefined) {
|
|
155
|
+
body.parent = undefined;
|
|
156
|
+
return body;
|
|
157
|
+
}
|
|
158
|
+
_modifyChildFromTo(parent, app, body);
|
|
159
|
+
return root;
|
|
160
|
+
}
|
|
161
|
+
/** Count IRVar(p) references in `body` accurately. Caps at 2 — we
|
|
162
|
+
* only care about 0/1/many — but ALWAYS walks the whole tree, so we
|
|
163
|
+
* correctly distinguish "1 trapped use" from "1 trapped + 1 more".
|
|
164
|
+
*/
|
|
165
|
+
function countVarUses(body, p) {
|
|
166
|
+
let count = 0;
|
|
167
|
+
let firstUseTrapped = false;
|
|
168
|
+
const stack = [{ node: body, trapped: false }];
|
|
169
|
+
while (stack.length > 0) {
|
|
170
|
+
const { node: t, trapped } = stack.pop();
|
|
171
|
+
if (t instanceof IRVar) {
|
|
172
|
+
if (t.name === p) {
|
|
173
|
+
if (count === 0)
|
|
174
|
+
firstUseTrapped = trapped;
|
|
175
|
+
count++;
|
|
176
|
+
if (count >= 2)
|
|
177
|
+
return { count: 2, trapped: false };
|
|
178
|
+
}
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
// Symbols are minted fresh on each binder, so shadowing is rare,
|
|
182
|
+
// but the guard is cheap and protects against any reuse.
|
|
183
|
+
if (t instanceof IRFunc && t.params.includes(p))
|
|
184
|
+
continue;
|
|
185
|
+
if (t instanceof IRRecursive && t.name === p)
|
|
186
|
+
continue;
|
|
187
|
+
// Determine the trap state for descending into this node:
|
|
188
|
+
// - IRRecursive always traps (loop introduces multi-eval).
|
|
189
|
+
// - IRFunc traps EXCEPT when it's a direct case continuation
|
|
190
|
+
// (`IRCase(_, […, this IRFunc, …])`): the case dispatches
|
|
191
|
+
// it at most once per case eval, so it's transparent.
|
|
192
|
+
let childTrapped = trapped;
|
|
193
|
+
if (t instanceof IRRecursive)
|
|
194
|
+
childTrapped = true;
|
|
195
|
+
else if (t instanceof IRFunc) {
|
|
196
|
+
const parent = t.parent;
|
|
197
|
+
const isCaseContinuation = parent instanceof IRCase
|
|
198
|
+
// direct continuation array membership
|
|
199
|
+
&& Array.from(parent.continuations).includes(t);
|
|
200
|
+
if (!isCaseContinuation)
|
|
201
|
+
childTrapped = true;
|
|
202
|
+
}
|
|
203
|
+
for (const c of t.children())
|
|
204
|
+
stack.push({ node: c, trapped: childTrapped });
|
|
205
|
+
}
|
|
206
|
+
return { count: count, trapped: firstUseTrapped };
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Replace EVERY `IRVar(p)` inside `body` with a fresh clone of
|
|
210
|
+
* `replacement`. Used for the var-arg beta-reduction: substituting one
|
|
211
|
+
* variable name for another anywhere it appears.
|
|
212
|
+
*
|
|
213
|
+
* Returns the (possibly new) body. If `body` itself is `IRVar(p)`,
|
|
214
|
+
* returns a fresh clone of `replacement` — the caller is responsible
|
|
215
|
+
* for wiring it into the parent.
|
|
216
|
+
*/
|
|
217
|
+
function substituteAllVar(body, p, replacement) {
|
|
218
|
+
if (body instanceof IRVar && body.name === p) {
|
|
219
|
+
return replacement.clone();
|
|
220
|
+
}
|
|
221
|
+
const stack = [body];
|
|
222
|
+
while (stack.length > 0) {
|
|
223
|
+
const t = stack.pop();
|
|
224
|
+
// Don't descend into scopes that shadow p (unique-symbol
|
|
225
|
+
// invariant makes this rare, but the guard is cheap).
|
|
226
|
+
if (t instanceof IRFunc && t.params.includes(p))
|
|
227
|
+
continue;
|
|
228
|
+
if (t instanceof IRRecursive && t.name === p)
|
|
229
|
+
continue;
|
|
230
|
+
// Snapshot children before mutating (children() returns a fresh
|
|
231
|
+
// array, so the loop is stable across modifications).
|
|
232
|
+
const children = t.children();
|
|
233
|
+
for (const child of children) {
|
|
234
|
+
if (child instanceof IRVar && child.name === p) {
|
|
235
|
+
// `t` must be a parent term because we got `child` from
|
|
236
|
+
// `t.children()`. Cast to satisfy the type checker.
|
|
237
|
+
_modifyChildFromTo(t, child, replacement.clone());
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
stack.push(child);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return body;
|
|
245
|
+
}
|
|
246
|
+
/** Replace the (single) IRVar(p) inside `body` with `replacement`. */
|
|
247
|
+
function substituteVar(body, p, replacement) {
|
|
248
|
+
const stack = [body];
|
|
249
|
+
while (stack.length > 0) {
|
|
250
|
+
const t = stack.pop();
|
|
251
|
+
if (t instanceof IRVar && t.name === p) {
|
|
252
|
+
const parent = t.parent;
|
|
253
|
+
if (parent !== undefined)
|
|
254
|
+
_modifyChildFromTo(parent, t, replacement);
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
if (t instanceof IRFunc && t.params.includes(p))
|
|
258
|
+
continue;
|
|
259
|
+
if (t instanceof IRRecursive && t.name === p)
|
|
260
|
+
continue;
|
|
261
|
+
stack.push(...t.children());
|
|
262
|
+
}
|
|
263
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* When the same list `L` is used for BOTH `headList(L)` and `tailList(L)`
|
|
3
|
+
* within a single function body, those two builtin calls can be replaced
|
|
4
|
+
* by a single `case L of cons h t -> body[h/headList(L), t/tailList(L)] |
|
|
5
|
+
* nil -> error`. The trade-off (measured in `bench.headTailVsCase`):
|
|
6
|
+
*
|
|
7
|
+
* - one `headList` (or `tailList`) builtin call: 112,100 CPU / 800 mem
|
|
8
|
+
* - one `case L of cons h t -> h | nil -> error`: 128,100 CPU / 900 mem
|
|
9
|
+
*
|
|
10
|
+
* So for a single access the builtin wins; for dual access the case wins
|
|
11
|
+
* by a single dispatch (~96K CPU / 700 mem).
|
|
12
|
+
*
|
|
13
|
+
* This pass walks every `IRFunc` body bottom-up. For each body, it scans
|
|
14
|
+
* the top-level (without descending into nested `IRFunc`/`IRRecursive`,
|
|
15
|
+
* because nested scopes are processed independently). If a free list var
|
|
16
|
+
* `L` has both a `headList(L)` and a `tailList(L)` use in the body, the
|
|
17
|
+
* body is wrapped with `IRCase(IRVar(L), [IRFunc([h, t], body'), IRError])`
|
|
18
|
+
* — where `body'` has those calls replaced with `IRVar(h)` / `IRVar(t)`.
|
|
19
|
+
*
|
|
20
|
+
* Notes:
|
|
21
|
+
* - We only act on `IRFunc` bodies that are NOT immediate continuations
|
|
22
|
+
* of an `IRCase` whose scrutinee is `IRVar(L)` for the same L — that
|
|
23
|
+
* case-cons branch already binds head/tail and the prior
|
|
24
|
+
* `rewriteHeadTailInCaseConsAndReturnRoot` pass has already done the
|
|
25
|
+
* substitution. Wrapping again would be a no-op constructor pair.
|
|
26
|
+
* - The nil branch is `IRError`. Original code that calls `headList` or
|
|
27
|
+
* `tailList` on a nil list errors at evaluation; the new code errors
|
|
28
|
+
* at the case dispatch — same observable behavior whenever either
|
|
29
|
+
* call is actually reached.
|
|
30
|
+
* - The pass iterates: after introducing a case for L, the (now
|
|
31
|
+
* substituted) body might still contain a different L' with dual
|
|
32
|
+
* head/tail uses — handled by re-scanning until no more pairs.
|
|
33
|
+
*/
|
|
34
|
+
import { IRTerm } from "../../IRTerm.js";
|
|
35
|
+
export declare function introduceCaseForDualHeadTailAndReturnRoot(term: IRTerm): IRTerm;
|