@fraczak/k 7.0.2 → 7.1.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/CHANGELOG.md +42 -0
- package/DOCS/FILE_FORMATS.md +238 -15
- package/DOCS/POLYMORPHIC_IR.md +104 -0
- package/JOURNAL.md +288 -0
- package/README.md +10 -2
- package/access-table.mjs +343 -0
- package/backend-api.mjs +18 -0
- package/backends/llvm/README.md +20 -21
- package/backends/llvm/bin/k-llvm-build.mjs +105 -32
- package/backends/llvm/bin/k-llvm-compile.mjs +63 -24
- package/backends/llvm/bin/k-llvm.mjs +292 -0
- package/backends/llvm/runtime/krt.c +8 -0
- package/backends/llvm/runtime/krt.h +1 -0
- package/backends/llvm/src/cli.mjs +95 -17
- package/backends/llvm/src/executable.mjs +128 -11
- package/backends/llvm/src/kvm2llvm.mjs +10 -5
- package/backends/llvm/src/llvm.mjs +121 -9
- package/backends/llvm/tests/integration.sh +11 -11
- package/backends/llvm/{bin → tests}/k-llvm-run.mjs +48 -22
- package/backends/llvm/tests/perf-support.mjs +24 -3
- package/backends/llvm/tests/test-llvm.mjs +22 -1
- package/backends/wasm/bin/k-wasm-compile.mjs +46 -20
- package/backends/wasm/bin/k-wasm-run.mjs +61 -20
- package/backends/wasm/bin/k-wasm.mjs +47 -13
- package/backends/wasm/src/cli.mjs +141 -19
- package/backends/wasm/src/wasm.mjs +130 -23
- package/browser/repl-browser.mjs +16 -5
- package/codecs/k-parse.mjs +29 -8
- package/codecs/k-print.mjs +22 -6
- package/codecs/runtime/codec-sdk.mjs +8 -4
- package/codecs/show.mjs +20 -6
- package/k.mjs +56 -16
- package/kir.mjs +58 -19
- package/kvm.mjs +49 -12
- package/objects/compile.mjs +37 -23
- package/objects/decompile.mjs +40 -10
- package/objects/extract-aliases.mjs +44 -14
- package/objects/inspect.mjs +19 -14
- package/objects/validate.mjs +18 -11
- package/package.json +10 -3
- package/pkir.mjs +772 -0
- package/repl.mjs +36 -18
- package/scripts/build-repl-html.mjs +14 -2
- package/scripts/perf-ieee.mjs +264 -13
- package/scripts/perf-int.mjs +315 -22
- package/scripts/perf-multiplication.mjs +58 -7
- package/scripts/perf-poly.mjs +306 -24
- package/scripts/run-tests.mjs +3 -0
- package/tests/test-access-table.mjs +213 -0
- package/tests/test-pkir.mjs +530 -0
- package/tests/test-polymorphic-tdd.mjs +196 -0
- package/tests/test-repl-html.mjs +15 -2
- package/tests/test-repl.mjs +30 -1
- package/backends/llvm/bin/k-llvm-jit.mjs +0 -160
package/backends/llvm/README.md
CHANGED
|
@@ -46,34 +46,33 @@ status in `k_result`. These operations can compose through KIR `comp`.
|
|
|
46
46
|
```sh
|
|
47
47
|
k-llvm-build [options] [source-snippet | input-file [output-exe]]
|
|
48
48
|
k-llvm-compile [options] [source-snippet | input-file [output.ll]]
|
|
49
|
-
k-llvm
|
|
50
|
-
k-llvm-run [options] object.ko [input.kv]
|
|
49
|
+
k-llvm [options] ( source-snippet | input-file ) [ input-wire-file ]
|
|
51
50
|
```
|
|
52
51
|
|
|
53
52
|
Options:
|
|
54
53
|
|
|
55
54
|
- `-o, --output path`: specify output file path explicitly.
|
|
56
55
|
- `--lib file`: load one `.klib` or `.k` dependency before compiling.
|
|
57
|
-
- `--export spec`: export a library alias into
|
|
58
|
-
- `--
|
|
59
|
-
- `--retype rel`:
|
|
60
|
-
- `--
|
|
61
|
-
- `-h
|
|
62
|
-
|
|
63
|
-
The compile commands follow the unified k CLI input convention: pass
|
|
64
|
-
source, an existing `.k` source file, or a `.ko`/`.klib` object.
|
|
65
|
-
source from stdin when input is omitted. Output paths can be
|
|
66
|
-
positional argument or explicitly with `-o, --output`.
|
|
67
|
-
Without `--expect`, `k-llvm-run` prints the result value as compact JSON.
|
|
56
|
+
- `--export spec`: export a library alias into scope. Repeatable.
|
|
57
|
+
- `--pkir`: lower via Polymorphic K IR (PKIR).
|
|
58
|
+
- `--retype rel`: relation to specialize (defaults to object main).
|
|
59
|
+
- `--opt flag`: Clang optimization flag (-O0, -O1, -O2, -O3).
|
|
60
|
+
- `-h, --help`: show usage.
|
|
61
|
+
|
|
62
|
+
The compile commands follow the unified k CLI input convention: pass
|
|
63
|
+
inline source, an existing `.k` source file, or a `.ko`/`.klib` object.
|
|
64
|
+
Reads UTF-8 source from stdin when input is omitted. Output paths can be
|
|
65
|
+
specified as a positional argument or explicitly with `-o, --output`.
|
|
68
66
|
`k-llvm-build` emits a native executable that reads and writes the binary
|
|
69
|
-
k pattern+value envelope used by `codecs/k-parse.mjs` and
|
|
70
|
-
The executable checks the stdin envelope pattern
|
|
71
|
-
pattern and encodes stdout with the compiled
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
67
|
+
k pattern+value envelope used by `codecs/k-parse.mjs` and
|
|
68
|
+
`codecs/k-print.mjs`. The executable checks the stdin envelope pattern
|
|
69
|
+
against the compiled input pattern and encodes stdout with the compiled
|
|
70
|
+
output pattern.
|
|
71
|
+
`k-llvm` reads the binary wire envelope, specializes the relation to
|
|
72
|
+
that input pattern, caches the compiled native executable, and writes
|
|
73
|
+
the compiled output envelope to stdout.
|
|
74
|
+
The native envelope codec supports the k Unicode string encoding used
|
|
75
|
+
for labels and tags.
|
|
77
76
|
|
|
78
77
|
## Tests
|
|
79
78
|
|
|
@@ -9,35 +9,61 @@ import {
|
|
|
9
9
|
} from "../src/cli.mjs";
|
|
10
10
|
import {
|
|
11
11
|
compileKVMToExecutable,
|
|
12
|
-
compileObjectToExecutable
|
|
12
|
+
compileObjectToExecutable,
|
|
13
|
+
compilePKIRToExecutable
|
|
13
14
|
} from "../src/executable.mjs";
|
|
14
15
|
|
|
15
|
-
function
|
|
16
|
+
function helpText() {
|
|
16
17
|
const prog = argv[1] || "k-llvm-build.mjs";
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
18
|
+
return [
|
|
19
|
+
"Compile k source, .ko, .klib, .kvm, or .pkir into a native",
|
|
20
|
+
"standalone executable.",
|
|
21
|
+
"",
|
|
22
|
+
`Usage: ${prog} [options]`,
|
|
23
|
+
" [source-snippet | input-file [output-exe]]",
|
|
24
|
+
"",
|
|
25
|
+
"Arguments:",
|
|
26
|
+
" source-snippet Inline k source snippet (in the style of k.mjs).",
|
|
27
|
+
" input-file Source .k, .ko, .klib, .kvm, or .pkir file.",
|
|
28
|
+
" Reads UTF-8 source from stdin when omitted.",
|
|
29
|
+
" output-exe Output executable path.",
|
|
30
|
+
"",
|
|
31
|
+
"The executable reads a binary k pattern+value envelope from stdin",
|
|
32
|
+
"and writes a binary k pattern+value envelope to stdout.",
|
|
33
|
+
"The stdin envelope pattern must match the compiled input pattern;",
|
|
34
|
+
"stdout is encoded with the compiled output pattern.",
|
|
35
|
+
"",
|
|
36
|
+
"Options:",
|
|
37
|
+
" -o, --output path Specify output file path explicitly.",
|
|
38
|
+
" --pkir Lower via Polymorphic K IR (PKIR).",
|
|
39
|
+
" --input-pattern pattern Input pattern JSON or file for spec.",
|
|
40
|
+
" --retype rel Relation to specialize (default: main).",
|
|
41
|
+
" --lib file Load .klib or .k library before build.",
|
|
42
|
+
" --export spec Export library alias into scope. Repeatable.",
|
|
43
|
+
" spec is 'name' or 'libname:localname'.",
|
|
44
|
+
" --opt flag Clang optimization flag (default: -O3).",
|
|
45
|
+
" -h, --help Show this help.",
|
|
46
|
+
"",
|
|
47
|
+
"Environment Variables:",
|
|
48
|
+
" CLANG, CC Path to Clang compiler (default: clang).",
|
|
49
|
+
" K_LLVM_CLANG_OPT Clang optimization flag (default: -O3).",
|
|
50
|
+
" K_LLVM_RUNTIME_MODE Runtime mode: fast, compact (default: fast).",
|
|
51
|
+
"",
|
|
52
|
+
"Examples:",
|
|
53
|
+
` ${prog} 'x = |x; x x' -o program.exe`,
|
|
54
|
+
` ${prog} program.k program.exe`,
|
|
55
|
+
` ${prog} --lib Examples/poly.k --export concat 's' -o s.exe`
|
|
56
|
+
].join("\n");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function usage(stream = console.error) {
|
|
60
|
+
stream(helpText());
|
|
37
61
|
}
|
|
38
62
|
|
|
39
63
|
function readMaybeFile(textOrPath) {
|
|
40
|
-
return fs.existsSync(textOrPath)
|
|
64
|
+
return fs.existsSync(textOrPath)
|
|
65
|
+
? fs.readFileSync(textOrPath, "utf8")
|
|
66
|
+
: textOrPath;
|
|
41
67
|
}
|
|
42
68
|
|
|
43
69
|
function readPattern(inputPattern) {
|
|
@@ -54,17 +80,38 @@ try {
|
|
|
54
80
|
|
|
55
81
|
let explicitOutput = null;
|
|
56
82
|
let rawInputPattern = null;
|
|
83
|
+
let relation = null;
|
|
84
|
+
let clangOpt = process.env.K_LLVM_CLANG_OPT || "-O3";
|
|
85
|
+
let runtimeMode = process.env.K_LLVM_RUNTIME_MODE || "fast";
|
|
57
86
|
const remainingArgs = [];
|
|
58
87
|
while (args.length > 0) {
|
|
59
88
|
const arg = args[0];
|
|
60
89
|
if (arg === "-o" || arg === "--output") {
|
|
61
90
|
args.shift();
|
|
62
91
|
explicitOutput = args.shift();
|
|
63
|
-
if (!explicitOutput)
|
|
92
|
+
if (!explicitOutput) {
|
|
93
|
+
throw new Error("-o/--output requires a file argument");
|
|
94
|
+
}
|
|
64
95
|
} else if (arg === "--input-pattern") {
|
|
65
96
|
args.shift();
|
|
66
97
|
rawInputPattern = args.shift();
|
|
67
|
-
if (!rawInputPattern)
|
|
98
|
+
if (!rawInputPattern) {
|
|
99
|
+
throw new Error(
|
|
100
|
+
"--input-pattern requires a JSON or file argument"
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
} else if (arg === "--retype") {
|
|
104
|
+
args.shift();
|
|
105
|
+
relation = args.shift();
|
|
106
|
+
if (!relation) {
|
|
107
|
+
throw new Error("--retype requires a relation name");
|
|
108
|
+
}
|
|
109
|
+
} else if (arg === "--opt") {
|
|
110
|
+
args.shift();
|
|
111
|
+
clangOpt = args.shift();
|
|
112
|
+
if (!clangOpt) {
|
|
113
|
+
throw new Error("--opt requires a flag (e.g. -O3)");
|
|
114
|
+
}
|
|
68
115
|
} else {
|
|
69
116
|
remainingArgs.push(args.shift());
|
|
70
117
|
}
|
|
@@ -72,17 +119,38 @@ try {
|
|
|
72
119
|
|
|
73
120
|
const inputPattern = readPattern(rawInputPattern);
|
|
74
121
|
|
|
75
|
-
const { libraries, exportSpecs } =
|
|
76
|
-
|
|
122
|
+
const { libraries, exportSpecs, usePKIR } =
|
|
123
|
+
parseCompileOptions(remainingArgs);
|
|
124
|
+
const input = resolveProgramInput(remainingArgs, {
|
|
125
|
+
allowStdinSource: true
|
|
126
|
+
});
|
|
77
127
|
const positionalOutput = remainingArgs.shift();
|
|
78
128
|
if (remainingArgs.length > 0) throw new Error("Too many arguments");
|
|
79
129
|
|
|
80
130
|
const outputPath = explicitOutput || positionalOutput || null;
|
|
81
|
-
if (outputPath == null)
|
|
131
|
+
if (outputPath == null) {
|
|
132
|
+
throw new Error(
|
|
133
|
+
"Output executable path required (pass output-exe or -o/--output)"
|
|
134
|
+
);
|
|
135
|
+
}
|
|
82
136
|
|
|
83
|
-
if (input.kind === "
|
|
137
|
+
if (input.kind === "pkir") {
|
|
138
|
+
const pkir = JSON.parse(fs.readFileSync(input.path, "utf8"));
|
|
139
|
+
compilePKIRToExecutable(pkir, outputPath, {
|
|
140
|
+
entry: relation,
|
|
141
|
+
inputPattern,
|
|
142
|
+
clangOpt,
|
|
143
|
+
runtimeMode
|
|
144
|
+
});
|
|
145
|
+
} else if (input.kind === "kvm") {
|
|
84
146
|
const kvm = JSON.parse(fs.readFileSync(input.path, "utf8"));
|
|
85
|
-
compileKVMToExecutable(kvm, outputPath, {
|
|
147
|
+
compileKVMToExecutable(kvm, outputPath, {
|
|
148
|
+
entry: relation,
|
|
149
|
+
inputPattern,
|
|
150
|
+
clangOpt,
|
|
151
|
+
runtimeMode,
|
|
152
|
+
usePKIR
|
|
153
|
+
});
|
|
86
154
|
} else {
|
|
87
155
|
const object = await compileProgramInputToObject(input, {
|
|
88
156
|
libraries,
|
|
@@ -90,10 +158,15 @@ try {
|
|
|
90
158
|
stdin
|
|
91
159
|
});
|
|
92
160
|
|
|
93
|
-
compileObjectToExecutable(object, outputPath, {
|
|
161
|
+
compileObjectToExecutable(object, outputPath, {
|
|
162
|
+
relation: relation || object.main,
|
|
163
|
+
inputPattern,
|
|
164
|
+
clangOpt,
|
|
165
|
+
runtimeMode,
|
|
166
|
+
usePKIR
|
|
167
|
+
});
|
|
94
168
|
}
|
|
95
169
|
} catch (error) {
|
|
96
170
|
console.error(error.stack || error.message || String(error));
|
|
97
|
-
usage();
|
|
98
171
|
exit(1);
|
|
99
172
|
}
|
|
@@ -9,26 +9,44 @@ import {
|
|
|
9
9
|
} from "../src/cli.mjs";
|
|
10
10
|
import {
|
|
11
11
|
compileObjectToLLVM,
|
|
12
|
-
compileLLVMArtifactFromKVM
|
|
12
|
+
compileLLVMArtifactFromKVM,
|
|
13
|
+
compileLLVMArtifactFromPKIR
|
|
13
14
|
} from "../src/llvm.mjs";
|
|
14
15
|
import { inputPatternForObjectRelation } from "../src/executable.mjs";
|
|
15
16
|
|
|
16
|
-
function
|
|
17
|
+
function helpText() {
|
|
17
18
|
const prog = argv[1] || "k-llvm-compile.mjs";
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
19
|
+
return [
|
|
20
|
+
"Compile a k .ko/.klib object, source, .kvm, or .pkir into LLVM IR.",
|
|
21
|
+
"",
|
|
22
|
+
`Usage: ${prog} [options]`,
|
|
23
|
+
" [source-snippet | input-file [output.ll]]",
|
|
24
|
+
"",
|
|
25
|
+
"Arguments:",
|
|
26
|
+
" source-snippet Inline k source snippet (in the style of k.mjs).",
|
|
27
|
+
" input-file Source .k, .ko, .klib, .kvm, or .pkir file.",
|
|
28
|
+
" Reads UTF-8 source from stdin when omitted.",
|
|
29
|
+
" output.ll Output LLVM IR path (default: stdout).",
|
|
30
|
+
"",
|
|
31
|
+
"Options:",
|
|
32
|
+
" -o, --output path Specify output file path explicitly.",
|
|
33
|
+
" --pkir Lower via Polymorphic K IR (PKIR).",
|
|
34
|
+
" --retype rel Relation to compile (default: object main).",
|
|
35
|
+
" --lib file Load one .klib or .k library before compiling.",
|
|
36
|
+
" --export spec Export library alias into scope. Repeatable.",
|
|
37
|
+
" spec is 'name' or 'libname:localname'.",
|
|
38
|
+
" -h, --help Show this help.",
|
|
39
|
+
"",
|
|
40
|
+
"Examples:",
|
|
41
|
+
` ${prog} 'x = |x; x x' program.ll`,
|
|
42
|
+
` ${prog} program.k -o program.ll`,
|
|
43
|
+
` ${prog} --pkir program.k -o program.ll`,
|
|
44
|
+
` ${prog} --lib core.klib --export add program.k program.ll`
|
|
45
|
+
].join("\n");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function usage(stream = console.error) {
|
|
49
|
+
stream(helpText());
|
|
32
50
|
}
|
|
33
51
|
|
|
34
52
|
try {
|
|
@@ -39,29 +57,49 @@ try {
|
|
|
39
57
|
}
|
|
40
58
|
|
|
41
59
|
let explicitOutput = null;
|
|
60
|
+
let relation = null;
|
|
42
61
|
const remainingArgs = [];
|
|
43
62
|
while (args.length > 0) {
|
|
44
63
|
const arg = args[0];
|
|
45
64
|
if (arg === "-o" || arg === "--output") {
|
|
46
65
|
args.shift();
|
|
47
66
|
explicitOutput = args.shift();
|
|
48
|
-
if (!explicitOutput)
|
|
67
|
+
if (!explicitOutput) {
|
|
68
|
+
throw new Error("-o/--output requires a file argument");
|
|
69
|
+
}
|
|
70
|
+
} else if (arg === "--retype") {
|
|
71
|
+
args.shift();
|
|
72
|
+
relation = args.shift();
|
|
73
|
+
if (!relation) {
|
|
74
|
+
throw new Error("--retype requires a relation name");
|
|
75
|
+
}
|
|
49
76
|
} else {
|
|
50
77
|
remainingArgs.push(args.shift());
|
|
51
78
|
}
|
|
52
79
|
}
|
|
53
80
|
|
|
54
|
-
const { libraries, exportSpecs } =
|
|
55
|
-
|
|
81
|
+
const { libraries, exportSpecs, usePKIR } =
|
|
82
|
+
parseCompileOptions(remainingArgs);
|
|
83
|
+
const input = resolveProgramInput(remainingArgs, {
|
|
84
|
+
allowStdinSource: true
|
|
85
|
+
});
|
|
56
86
|
const positionalOutput = remainingArgs.shift();
|
|
57
87
|
if (remainingArgs.length > 0) throw new Error("Too many arguments");
|
|
58
88
|
|
|
59
89
|
const outputPath = explicitOutput || positionalOutput || null;
|
|
60
90
|
|
|
61
91
|
let llvm;
|
|
62
|
-
if (input.kind === "
|
|
92
|
+
if (input.kind === "pkir") {
|
|
93
|
+
const pkir = JSON.parse(fs.readFileSync(input.path, "utf8"));
|
|
94
|
+
const entry = relation || pkir.entry || pkir.name || "__main__";
|
|
95
|
+
llvm = compileLLVMArtifactFromPKIR(pkir, {
|
|
96
|
+
relation: entry,
|
|
97
|
+
inputPattern: pkir.inputPattern,
|
|
98
|
+
outputPattern: pkir.outputPattern
|
|
99
|
+
});
|
|
100
|
+
} else if (input.kind === "kvm") {
|
|
63
101
|
const kvm = JSON.parse(fs.readFileSync(input.path, "utf8"));
|
|
64
|
-
const entry = kvm.entry || kvm.relation || "__main__";
|
|
102
|
+
const entry = relation || kvm.entry || kvm.relation || "__main__";
|
|
65
103
|
const mainFunc = kvm.functions?.[entry];
|
|
66
104
|
const artifact = compileLLVMArtifactFromKVM(kvm, {
|
|
67
105
|
relation: entry,
|
|
@@ -76,9 +114,11 @@ try {
|
|
|
76
114
|
stdin
|
|
77
115
|
});
|
|
78
116
|
|
|
117
|
+
const targetRel = relation || object.main;
|
|
79
118
|
const res = compileObjectToLLVM(object, {
|
|
80
|
-
relation:
|
|
81
|
-
inputPattern: inputPatternForObjectRelation(object,
|
|
119
|
+
relation: targetRel,
|
|
120
|
+
inputPattern: inputPatternForObjectRelation(object, targetRel),
|
|
121
|
+
usePKIR
|
|
82
122
|
});
|
|
83
123
|
llvm = res.llvm;
|
|
84
124
|
}
|
|
@@ -90,6 +130,5 @@ try {
|
|
|
90
130
|
}
|
|
91
131
|
} catch (error) {
|
|
92
132
|
console.error(error.stack || error.message || String(error));
|
|
93
|
-
usage();
|
|
94
133
|
exit(1);
|
|
95
134
|
}
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import crypto from "node:crypto";
|
|
4
|
+
import fs from "node:fs";
|
|
5
|
+
import os from "node:os";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { spawn } from "node:child_process";
|
|
8
|
+
import { argv, exit, stdin, stdout } from "node:process";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
10
|
+
|
|
11
|
+
import { decodeWire } from "@fraczak/k/backend-api.mjs";
|
|
12
|
+
import {
|
|
13
|
+
compileProgramInputToObject,
|
|
14
|
+
parseCompileOptions,
|
|
15
|
+
readAll,
|
|
16
|
+
resolveProgramInput
|
|
17
|
+
} from "../src/cli.mjs";
|
|
18
|
+
import {
|
|
19
|
+
compileKVMToExecutable,
|
|
20
|
+
compileObjectToExecutable,
|
|
21
|
+
compilePKIRToExecutable
|
|
22
|
+
} from "../src/executable.mjs";
|
|
23
|
+
|
|
24
|
+
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
25
|
+
|
|
26
|
+
function helpText() {
|
|
27
|
+
const prog = argv[1] || "k-llvm.mjs";
|
|
28
|
+
const defaultCache = path.join(os.tmpdir(), "k-llvm-cache");
|
|
29
|
+
return [
|
|
30
|
+
"Compile k source, .ko, .kvm, or .pkir to native machine code via",
|
|
31
|
+
"LLVM and execute it over a binary pattern+value wire stream.",
|
|
32
|
+
"",
|
|
33
|
+
`Usage: ${prog} [options]`,
|
|
34
|
+
" ( source-snippet | input-file ) [ input-wire-file ]",
|
|
35
|
+
"",
|
|
36
|
+
"Arguments:",
|
|
37
|
+
" source-snippet Inline k source snippet (in the style of k.mjs).",
|
|
38
|
+
" input-file Source .k, .ko, .klib, .kvm, or .pkir file.",
|
|
39
|
+
" input-wire-file Optional binary wire input (default: stdin).",
|
|
40
|
+
"",
|
|
41
|
+
"Options:",
|
|
42
|
+
" --lib file Load one .klib or .k library before compiling.",
|
|
43
|
+
" --export spec Export library alias into scope. Repeatable.",
|
|
44
|
+
" spec is 'name' or 'libname:localname'.",
|
|
45
|
+
" --pkir Lower via Polymorphic K IR (PKIR).",
|
|
46
|
+
" --retype rel Relation to specialize (default: object main).",
|
|
47
|
+
" --cache-dir path Directory to store compiled native executables.",
|
|
48
|
+
" --no-cache Compile in a temporary directory and discard.",
|
|
49
|
+
" --opt flag Clang optimization level (-O0, -O1, -O2, -O3).",
|
|
50
|
+
" -h, --help Show this help.",
|
|
51
|
+
"",
|
|
52
|
+
"Environment Variables:",
|
|
53
|
+
` K_LLVM_CACHE_DIR Cache directory (default: ${defaultCache}).`,
|
|
54
|
+
" K_LLVM_CLANG_OPT Default Clang optimization flag (default: -O1).",
|
|
55
|
+
" CLANG, CC Path to Clang compiler binary (default: clang).",
|
|
56
|
+
" K_TRACE, TRACE When '1', output phase tracing to stderr.",
|
|
57
|
+
"",
|
|
58
|
+
"Examples:",
|
|
59
|
+
` echo '"abc"' | k-parse | ${prog} '{.1 0}' | k-print`,
|
|
60
|
+
` echo '{"x":1,"y":2}' | k-json --parse | \\`,
|
|
61
|
+
` ${prog} --lib Examples/poly.k --export concat \\`,
|
|
62
|
+
` 's={()xs,()ys}concat; s' | k-json --print`
|
|
63
|
+
].join("\n");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function usage(stream = console.error) {
|
|
67
|
+
stream(helpText());
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
let cachedFingerprint = null;
|
|
71
|
+
function getBackendFingerprint() {
|
|
72
|
+
if (cachedFingerprint != null) return cachedFingerprint;
|
|
73
|
+
const hash = crypto.createHash("sha256");
|
|
74
|
+
for (const relPath of [
|
|
75
|
+
"src/llvm.mjs",
|
|
76
|
+
"src/kvm2llvm.mjs",
|
|
77
|
+
"src/executable.mjs",
|
|
78
|
+
"runtime/krt.c",
|
|
79
|
+
"runtime/krt.h"
|
|
80
|
+
]) {
|
|
81
|
+
const full = path.join(root, relPath);
|
|
82
|
+
if (fs.existsSync(full)) {
|
|
83
|
+
hash.update(relPath);
|
|
84
|
+
hash.update("\0");
|
|
85
|
+
hash.update(fs.readFileSync(full));
|
|
86
|
+
hash.update("\0");
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
cachedFingerprint = hash.digest("hex").slice(0, 16);
|
|
90
|
+
return cachedFingerprint;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function computeCacheKey({
|
|
94
|
+
fingerprint,
|
|
95
|
+
inputKey,
|
|
96
|
+
relation,
|
|
97
|
+
inputPattern,
|
|
98
|
+
usePKIR,
|
|
99
|
+
clangOpt
|
|
100
|
+
}) {
|
|
101
|
+
const hash = crypto.createHash("sha256");
|
|
102
|
+
hash.update("k-llvm-runner-v1\0");
|
|
103
|
+
hash.update(fingerprint);
|
|
104
|
+
hash.update("\0");
|
|
105
|
+
hash.update(String(relation || ""));
|
|
106
|
+
hash.update("\0");
|
|
107
|
+
hash.update(JSON.stringify(inputPattern || null));
|
|
108
|
+
hash.update("\0");
|
|
109
|
+
hash.update(usePKIR ? "pkir\0" : "monomorphic\0");
|
|
110
|
+
hash.update(String(clangOpt || ""));
|
|
111
|
+
hash.update("\0");
|
|
112
|
+
hash.update(inputKey);
|
|
113
|
+
return hash.digest("hex");
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function runNativeExecutable(exePath, inputBuffer) {
|
|
117
|
+
return new Promise((resolve, reject) => {
|
|
118
|
+
const child = spawn(exePath, [], {
|
|
119
|
+
stdio: ["pipe", "pipe", "inherit"]
|
|
120
|
+
});
|
|
121
|
+
const chunks = [];
|
|
122
|
+
child.stdout.on("data", (chunk) => chunks.push(chunk));
|
|
123
|
+
child.on("error", reject);
|
|
124
|
+
child.on("close", (status) => {
|
|
125
|
+
if (status !== 0) {
|
|
126
|
+
reject(new Error(`${exePath} exited with status ${status}`));
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
resolve(Buffer.concat(chunks));
|
|
130
|
+
});
|
|
131
|
+
child.stdin.end(inputBuffer);
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async function main() {
|
|
136
|
+
const rawArgs = argv.slice(2);
|
|
137
|
+
if (rawArgs.includes("-h") || rawArgs.includes("--help")) {
|
|
138
|
+
usage(console.log);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
let cacheDir = process.env.K_LLVM_CACHE_DIR ||
|
|
143
|
+
path.join(os.tmpdir(), "k-llvm-cache");
|
|
144
|
+
let useCache = true;
|
|
145
|
+
let relation = null;
|
|
146
|
+
let clangOpt = process.env.K_LLVM_CLANG_OPT || "-O1";
|
|
147
|
+
|
|
148
|
+
const remainingArgs = [];
|
|
149
|
+
while (rawArgs.length > 0) {
|
|
150
|
+
const arg = rawArgs[0];
|
|
151
|
+
if (arg === "--cache-dir") {
|
|
152
|
+
rawArgs.shift();
|
|
153
|
+
cacheDir = rawArgs.shift();
|
|
154
|
+
if (!cacheDir) {
|
|
155
|
+
throw new Error("--cache-dir requires a directory path");
|
|
156
|
+
}
|
|
157
|
+
} else if (arg === "--no-cache") {
|
|
158
|
+
rawArgs.shift();
|
|
159
|
+
useCache = false;
|
|
160
|
+
} else if (arg === "--retype") {
|
|
161
|
+
rawArgs.shift();
|
|
162
|
+
relation = rawArgs.shift();
|
|
163
|
+
if (!relation) {
|
|
164
|
+
throw new Error("--retype requires a relation name");
|
|
165
|
+
}
|
|
166
|
+
} else if (arg === "--opt") {
|
|
167
|
+
rawArgs.shift();
|
|
168
|
+
clangOpt = rawArgs.shift();
|
|
169
|
+
if (!clangOpt) {
|
|
170
|
+
throw new Error("--opt requires a flag (e.g. -O1)");
|
|
171
|
+
}
|
|
172
|
+
} else {
|
|
173
|
+
remainingArgs.push(rawArgs.shift());
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const { libraries, exportSpecs, usePKIR } =
|
|
178
|
+
parseCompileOptions(remainingArgs);
|
|
179
|
+
const programInput = resolveProgramInput(remainingArgs);
|
|
180
|
+
const inputWirePath = remainingArgs.shift();
|
|
181
|
+
if (remainingArgs.length > 0) {
|
|
182
|
+
throw new Error(`Unexpected argument: ${remainingArgs[0]}`);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const inputBuffer = await readAll(
|
|
186
|
+
inputWirePath == null ? stdin : fs.createReadStream(inputWirePath)
|
|
187
|
+
);
|
|
188
|
+
if (inputBuffer.length === 0) {
|
|
189
|
+
throw new Error("No input wire data received on stdin");
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const { pattern: inputEnvelopePattern } = decodeWire(inputBuffer);
|
|
193
|
+
|
|
194
|
+
let exePath;
|
|
195
|
+
let tempExeDir = null;
|
|
196
|
+
|
|
197
|
+
if (useCache) {
|
|
198
|
+
fs.mkdirSync(cacheDir, { recursive: true });
|
|
199
|
+
const fingerprint = getBackendFingerprint();
|
|
200
|
+
const inputKey = JSON.stringify({
|
|
201
|
+
programInput,
|
|
202
|
+
libraries: libraries.map((lib) => Object.keys(lib.rels || {})),
|
|
203
|
+
exportSpecs
|
|
204
|
+
});
|
|
205
|
+
const key = computeCacheKey({
|
|
206
|
+
fingerprint,
|
|
207
|
+
inputKey,
|
|
208
|
+
relation,
|
|
209
|
+
inputPattern: inputEnvelopePattern,
|
|
210
|
+
usePKIR,
|
|
211
|
+
clangOpt
|
|
212
|
+
});
|
|
213
|
+
exePath = path.join(cacheDir, `${key}.exe`);
|
|
214
|
+
if (!fs.existsSync(exePath)) {
|
|
215
|
+
const tmpPath = path.join(cacheDir, `${key}.${process.pid}.tmp`);
|
|
216
|
+
if (programInput.kind === "pkir") {
|
|
217
|
+
const pkir = JSON.parse(
|
|
218
|
+
fs.readFileSync(programInput.path, "utf8")
|
|
219
|
+
);
|
|
220
|
+
compilePKIRToExecutable(pkir, tmpPath, {
|
|
221
|
+
inputPattern: inputEnvelopePattern,
|
|
222
|
+
clangOpt
|
|
223
|
+
});
|
|
224
|
+
} else if (programInput.kind === "kvm") {
|
|
225
|
+
const kvm = JSON.parse(
|
|
226
|
+
fs.readFileSync(programInput.path, "utf8")
|
|
227
|
+
);
|
|
228
|
+
compileKVMToExecutable(kvm, tmpPath, {
|
|
229
|
+
inputPattern: inputEnvelopePattern,
|
|
230
|
+
clangOpt,
|
|
231
|
+
usePKIR
|
|
232
|
+
});
|
|
233
|
+
} else {
|
|
234
|
+
const object = await compileProgramInputToObject(programInput, {
|
|
235
|
+
libraries,
|
|
236
|
+
exportSpecs,
|
|
237
|
+
stdin
|
|
238
|
+
});
|
|
239
|
+
compileObjectToExecutable(object, tmpPath, {
|
|
240
|
+
relation: relation || object.main,
|
|
241
|
+
inputPattern: inputEnvelopePattern,
|
|
242
|
+
clangOpt,
|
|
243
|
+
usePKIR
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
fs.renameSync(tmpPath, exePath);
|
|
247
|
+
}
|
|
248
|
+
} else {
|
|
249
|
+
tempExeDir = fs.mkdtempSync(path.join(os.tmpdir(), "k-llvm-run-"));
|
|
250
|
+
exePath = path.join(tempExeDir, "prog.exe");
|
|
251
|
+
if (programInput.kind === "pkir") {
|
|
252
|
+
const pkir = JSON.parse(fs.readFileSync(programInput.path, "utf8"));
|
|
253
|
+
compilePKIRToExecutable(pkir, exePath, {
|
|
254
|
+
inputPattern: inputEnvelopePattern,
|
|
255
|
+
clangOpt
|
|
256
|
+
});
|
|
257
|
+
} else if (programInput.kind === "kvm") {
|
|
258
|
+
const kvm = JSON.parse(fs.readFileSync(programInput.path, "utf8"));
|
|
259
|
+
compileKVMToExecutable(kvm, exePath, {
|
|
260
|
+
inputPattern: inputEnvelopePattern,
|
|
261
|
+
clangOpt,
|
|
262
|
+
usePKIR
|
|
263
|
+
});
|
|
264
|
+
} else {
|
|
265
|
+
const object = await compileProgramInputToObject(programInput, {
|
|
266
|
+
libraries,
|
|
267
|
+
exportSpecs,
|
|
268
|
+
stdin
|
|
269
|
+
});
|
|
270
|
+
compileObjectToExecutable(object, exePath, {
|
|
271
|
+
relation: relation || object.main,
|
|
272
|
+
inputPattern: inputEnvelopePattern,
|
|
273
|
+
clangOpt,
|
|
274
|
+
usePKIR
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
try {
|
|
280
|
+
const outputBuffer = await runNativeExecutable(exePath, inputBuffer);
|
|
281
|
+
stdout.write(outputBuffer);
|
|
282
|
+
} finally {
|
|
283
|
+
if (tempExeDir) {
|
|
284
|
+
fs.rmSync(tempExeDir, { recursive: true, force: true });
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
main().catch((error) => {
|
|
290
|
+
console.error(error.stack || error.message || String(error));
|
|
291
|
+
exit(1);
|
|
292
|
+
});
|