@fraczak/k 7.0.0 → 7.0.2
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 +50 -0
- package/DOCS/BIG_PICTURE.md +5 -5
- package/DOCS/FILE_FORMATS.md +5 -5
- package/DOCS/OBJECT_FILE_AND_PATTERN.md +9 -9
- package/DOCS/PATTERNS.md +3 -3
- package/DOCS/PROFILING_AND_TRACING.md +60 -72
- package/DOCS/REPL.md +3 -3
- package/DOCS/book/03-types-as-finite-automata.md +4 -4
- package/DOCS/book/06-values-in-memory.md +9 -9
- package/DOCS/book/08-operational-semantics-and-execution.md +1 -1
- package/DOCS/book/09-compiler-architecture.md +2 -2
- package/DOCS/book/10-from-ast-to-intermediate-representation.md +2 -2
- package/DOCS/book/14-canonical-serialization.md +1 -1
- package/Examples/arithmetics.k +2 -2
- package/Examples/bnat.k +1 -1
- package/Examples/karatsuba-mult.k +2 -2
- package/README.md +3 -8
- package/backend-api.mjs +9 -1
- package/backends/llvm/runtime/krt.c +33 -0
- package/backends/llvm/runtime/krt.h +1 -0
- package/backends/llvm/scripts/run-tests.mjs +5 -0
- package/backends/llvm/src/executable.mjs +4 -4
- package/backends/llvm/src/kvm2llvm.mjs +42 -19
- package/backends/llvm/src/llvm.mjs +30 -36
- package/backends/llvm/tests/integration.sh +2 -2
- package/backends/llvm/tests/perf-support.mjs +1 -55
- package/backends/llvm/tests/test-llvm.mjs +3 -3
- package/backends/llvm/tests/test-open-product-misalignment.mjs +140 -0
- package/backends/wasm/demo_ieee.mjs +2 -3
- package/backends/wasm/runtime.wat +157 -5
- package/backends/wasm/src/kvm2wasm.mjs +57 -34
- package/backends/wasm/src/wasm.mjs +40 -18
- package/backends/wasm/tests/perf-support.mjs +4 -4
- package/backends/wasm/tests/test-wasm.mjs +13 -14
- package/browser/vfs-data.mjs +6 -6
- package/core.k +8 -8
- package/harness/format.mjs +2 -2
- package/harness/registry.mjs +0 -2
- package/kir.mjs +299 -17
- package/package.json +1 -5
- package/scripts/benchmark-idempotency.mjs +12 -8
- package/scripts/benchmark.mjs +5 -9
- package/scripts/build-repl-html.mjs +2 -2
- package/scripts/perf-ieee.mjs +17 -118
- package/scripts/perf-int.mjs +313 -318
- package/scripts/perf-multiplication.mjs +10 -20
- package/scripts/perf-poly.mjs +29 -183
- package/tests/code-derivation/pattern-envelope.mjs +4 -4
- package/tests/test-kvm-polymorphic.mjs +2 -2
- package/tests/test-repl-html.mjs +15 -0
- package/tests/test-repl.mjs +10 -0
- package/backends/arm64/README.md +0 -103
- package/backends/arm64/bin/k-arm64-compile.mjs +0 -93
- package/backends/arm64/bin/k-arm64-run.mjs +0 -58
- package/backends/arm64/bin/k-arm64.mjs +0 -88
- package/backends/arm64/scripts/run-tests.mjs +0 -57
- package/backends/arm64/src/arm64.mjs +0 -229
- package/backends/arm64/src/cli.mjs +0 -155
- package/backends/arm64/src/kvm2arm64.mjs +0 -906
- package/backends/arm64/src/runtime.c +0 -744
- package/backends/arm64/tests/integration.sh +0 -112
- package/backends/arm64/tests/test-arm64.mjs +0 -240
- package/harness/adapters/arm64.mjs +0 -105
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import fs from "node:fs";
|
|
4
|
-
import { argv, exit, stdin, stdout } from "node:process";
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
compileProgramInput,
|
|
8
|
-
parseCompileOptions,
|
|
9
|
-
resolveProgramInput
|
|
10
|
-
} from "../src/cli.mjs";
|
|
11
|
-
|
|
12
|
-
function usage(stream = console.error) {
|
|
13
|
-
const prog = argv[1] || "k-arm64-compile.mjs";
|
|
14
|
-
stream(`Usage: node ${prog} [options] [source-snippet | input-file [output-file]]`);
|
|
15
|
-
stream("Compile k source, .ko, or .kvm input into a standalone Linux ARM64 executable or assembly.");
|
|
16
|
-
stream("");
|
|
17
|
-
stream("Arguments:");
|
|
18
|
-
stream(" source-snippet Inline k source, in the same style as k.mjs.");
|
|
19
|
-
stream(" input-file Source .k, .ko, or .kvm file. Reads UTF-8 source from stdin when omitted.");
|
|
20
|
-
stream(" output-file Output file path. Writes to stdout when omitted and --assembly is set.");
|
|
21
|
-
stream("");
|
|
22
|
-
stream("Options:");
|
|
23
|
-
stream(" -S, --assembly Emit GNU ARM64 assembly (.s) instead of an executable binary.");
|
|
24
|
-
stream(" -o, --output Specify output file path explicitly.");
|
|
25
|
-
stream(" --lib file Load one .klib dependency before compiling.");
|
|
26
|
-
stream(" --export spec Export a library alias into source scope. May be repeated.");
|
|
27
|
-
stream(" spec is 'name' or 'libname:localname'.");
|
|
28
|
-
stream(" -h, --help Show this help.");
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
try {
|
|
32
|
-
const args = argv.slice(2);
|
|
33
|
-
if (args.includes("-h") || args.includes("--help")) {
|
|
34
|
-
usage(console.log);
|
|
35
|
-
exit(0);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
let emitAssembly = false;
|
|
39
|
-
let explicitOutput = null;
|
|
40
|
-
// Extract ARM64-specific flags
|
|
41
|
-
const remainingArgs = [];
|
|
42
|
-
while (args.length > 0) {
|
|
43
|
-
const arg = args[0];
|
|
44
|
-
if (arg === "-S" || arg === "--assembly") {
|
|
45
|
-
emitAssembly = true;
|
|
46
|
-
args.shift();
|
|
47
|
-
} else if (arg === "-o" || arg === "--output") {
|
|
48
|
-
args.shift();
|
|
49
|
-
explicitOutput = args.shift();
|
|
50
|
-
if (!explicitOutput) throw new Error("-o/--output requires a file argument");
|
|
51
|
-
} else {
|
|
52
|
-
remainingArgs.push(args.shift());
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const { libraries, exportSpecs } = parseCompileOptions(remainingArgs);
|
|
57
|
-
const input = resolveProgramInput(remainingArgs, { allowStdinSource: true });
|
|
58
|
-
const positionalOutput = remainingArgs.shift();
|
|
59
|
-
if (remainingArgs.length > 0) throw new Error("Too many arguments");
|
|
60
|
-
|
|
61
|
-
const outputPath = explicitOutput || positionalOutput || null;
|
|
62
|
-
|
|
63
|
-
const result = await compileProgramInput(input, {
|
|
64
|
-
libraries,
|
|
65
|
-
exportSpecs,
|
|
66
|
-
stdin,
|
|
67
|
-
emit: emitAssembly ? "assembly" : null,
|
|
68
|
-
outputPath: (!emitAssembly && outputPath) ? outputPath : null
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
if (emitAssembly) {
|
|
72
|
-
const asmText = typeof result === "string" ? result : result.assembly;
|
|
73
|
-
if (outputPath == null) {
|
|
74
|
-
stdout.write(asmText);
|
|
75
|
-
} else {
|
|
76
|
-
fs.writeFileSync(outputPath, asmText, "utf8");
|
|
77
|
-
}
|
|
78
|
-
} else {
|
|
79
|
-
if (outputPath == null) {
|
|
80
|
-
if (Buffer.isBuffer(result)) {
|
|
81
|
-
stdout.write(result);
|
|
82
|
-
} else if (typeof result === "string") {
|
|
83
|
-
stdout.write(fs.readFileSync(result));
|
|
84
|
-
}
|
|
85
|
-
} else if (Buffer.isBuffer(result)) {
|
|
86
|
-
fs.writeFileSync(outputPath, result, { mode: 0o755 });
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
} catch (error) {
|
|
90
|
-
console.error(error.stack || error.message || String(error));
|
|
91
|
-
usage();
|
|
92
|
-
exit(1);
|
|
93
|
-
}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import fs from "node:fs";
|
|
4
|
-
import { argv, exit, stdin, stdout } from "node:process";
|
|
5
|
-
|
|
6
|
-
import { readAll } from "../src/cli.mjs";
|
|
7
|
-
import { runARM64Artifact } from "../src/arm64.mjs";
|
|
8
|
-
|
|
9
|
-
function usage(stream = console.error) {
|
|
10
|
-
const prog = argv[1] || "k-arm64-run.mjs";
|
|
11
|
-
stream(`Usage: node ${prog} [options] executable-file [input-file]`);
|
|
12
|
-
stream("Run a standalone k Linux ARM64 executable over a binary wire stream.");
|
|
13
|
-
stream("");
|
|
14
|
-
stream("Arguments:");
|
|
15
|
-
stream(" executable-file Input compiled ELF binary produced by k-arm64-compile.");
|
|
16
|
-
stream(" input-file Optional binary input wire file. Reads stdin when omitted.");
|
|
17
|
-
stream("");
|
|
18
|
-
stream("Options:");
|
|
19
|
-
stream(" --json Format output as JSON instead of binary wire format.");
|
|
20
|
-
stream(" -h, --help Show this help.");
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
async function main() {
|
|
24
|
-
const args = argv.slice(2);
|
|
25
|
-
if (args.includes("-h") || args.includes("--help")) {
|
|
26
|
-
usage(console.log);
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
let json = false;
|
|
31
|
-
const filteredArgs = [];
|
|
32
|
-
for (const arg of args) {
|
|
33
|
-
if (arg === "--json") {
|
|
34
|
-
json = true;
|
|
35
|
-
} else {
|
|
36
|
-
filteredArgs.push(arg);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const execPath = filteredArgs.shift();
|
|
41
|
-
if (!execPath) throw new Error("Missing executable artifact path");
|
|
42
|
-
const inputPath = filteredArgs.shift();
|
|
43
|
-
if (filteredArgs.length > 0) throw new Error("Too many arguments");
|
|
44
|
-
|
|
45
|
-
const input = await readAll(inputPath == null ? stdin : fs.createReadStream(inputPath));
|
|
46
|
-
const result = runARM64Artifact(execPath, input, { json });
|
|
47
|
-
if (json) {
|
|
48
|
-
stdout.write(result);
|
|
49
|
-
} else {
|
|
50
|
-
stdout.write(result);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
main().catch((error) => {
|
|
55
|
-
console.error(error.stack || error.message || String(error));
|
|
56
|
-
usage();
|
|
57
|
-
exit(1);
|
|
58
|
-
});
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import fs from "node:fs";
|
|
4
|
-
import { argv, exit, stdin, stdout } from "node:process";
|
|
5
|
-
import { decodeWire } from "../../../backend-api.mjs";
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
compileProgramInput,
|
|
9
|
-
parseCompileOptions,
|
|
10
|
-
readAll,
|
|
11
|
-
resolveProgramInput
|
|
12
|
-
} from "../src/cli.mjs";
|
|
13
|
-
import { runARM64Artifact } from "../src/arm64.mjs";
|
|
14
|
-
|
|
15
|
-
function usage(stream = console.error) {
|
|
16
|
-
const prog = argv[1] || "k-arm64.mjs";
|
|
17
|
-
stream(`Usage: node ${prog} [options] ( source-snippet | input-file ) [ input-file ]`);
|
|
18
|
-
stream("Compile k source, .ko, or .kvm input to ARM64 in memory and run it over a binary pattern+value stream.");
|
|
19
|
-
stream("");
|
|
20
|
-
stream("Options:");
|
|
21
|
-
stream(" --json Format output as JSON instead of binary wire format.");
|
|
22
|
-
stream(" --entry name Specify entry relation name.");
|
|
23
|
-
stream(" --lib file Load one .klib dependency before compiling.");
|
|
24
|
-
stream(" --export spec Export a library alias into source scope. May be repeated.");
|
|
25
|
-
stream(" spec is 'name' or 'libname:localname'.");
|
|
26
|
-
stream(" -h, --help Show this help.");
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
async function main() {
|
|
30
|
-
const args = argv.slice(2);
|
|
31
|
-
if (args.includes("-h") || args.includes("--help")) {
|
|
32
|
-
usage(console.log);
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
let json = false;
|
|
37
|
-
let entry = null;
|
|
38
|
-
const remainingArgs = [];
|
|
39
|
-
while (args.length > 0) {
|
|
40
|
-
const arg = args[0];
|
|
41
|
-
if (arg === "--json") {
|
|
42
|
-
json = true;
|
|
43
|
-
args.shift();
|
|
44
|
-
} else if (arg === "--entry") {
|
|
45
|
-
args.shift();
|
|
46
|
-
entry = args.shift();
|
|
47
|
-
if (!entry) throw new Error("--entry requires a name argument");
|
|
48
|
-
} else {
|
|
49
|
-
remainingArgs.push(args.shift());
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const { libraries, exportSpecs } = parseCompileOptions(remainingArgs);
|
|
54
|
-
const programInput = resolveProgramInput(remainingArgs);
|
|
55
|
-
const inputPath = remainingArgs.shift();
|
|
56
|
-
if (remainingArgs.length > 0) throw new Error("Too many arguments");
|
|
57
|
-
|
|
58
|
-
const inputBuffer = await readAll(inputPath == null ? stdin : fs.createReadStream(inputPath));
|
|
59
|
-
let inputEnvelopePattern = null;
|
|
60
|
-
if (inputBuffer.length > 0) {
|
|
61
|
-
try {
|
|
62
|
-
const decoded = decodeWire(inputBuffer);
|
|
63
|
-
inputEnvelopePattern = decoded.pattern;
|
|
64
|
-
} catch {
|
|
65
|
-
// Not a valid wire buffer or empty
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const artifact = await compileProgramInput(programInput, {
|
|
70
|
-
libraries,
|
|
71
|
-
exportSpecs,
|
|
72
|
-
inputEnvelopePattern,
|
|
73
|
-
entry
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
const output = runARM64Artifact(artifact, inputBuffer, { json });
|
|
77
|
-
if (json) {
|
|
78
|
-
stdout.write(output);
|
|
79
|
-
} else {
|
|
80
|
-
stdout.write(output);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
main().catch((error) => {
|
|
85
|
-
console.error(error.stack || error.message || String(error));
|
|
86
|
-
usage();
|
|
87
|
-
exit(1);
|
|
88
|
-
});
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import { spawnSync } from "node:child_process";
|
|
5
|
-
import { fileURLToPath } from "node:url";
|
|
6
|
-
|
|
7
|
-
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
8
|
-
const node = process.execPath;
|
|
9
|
-
|
|
10
|
-
function printHelp() {
|
|
11
|
-
console.log(`Usage: node backends/arm64/scripts/run-tests.mjs [options]
|
|
12
|
-
|
|
13
|
-
Runs ARM64 backend test suite and integration tests.
|
|
14
|
-
|
|
15
|
-
Options:
|
|
16
|
-
-h, --help
|
|
17
|
-
Show this help message and exit.`);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
if (process.argv.includes("-h") || process.argv.includes("--help")) {
|
|
21
|
-
printHelp();
|
|
22
|
-
process.exit(0);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const tests = [
|
|
26
|
-
["tests/test-arm64.mjs", node, ["tests/test-arm64.mjs"]],
|
|
27
|
-
["tests/integration.sh", path.join(root, "tests/integration.sh"), []]
|
|
28
|
-
];
|
|
29
|
-
|
|
30
|
-
function formatDuration(milliseconds) {
|
|
31
|
-
if (milliseconds < 1000) return `${milliseconds.toFixed(0)} ms`;
|
|
32
|
-
return `${(milliseconds / 1000).toFixed(2)} s`;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const suiteStarted = performance.now();
|
|
36
|
-
for (const [name, command, args] of tests) {
|
|
37
|
-
console.log(`\n==> ${name}`);
|
|
38
|
-
const started = performance.now();
|
|
39
|
-
const result = spawnSync(command, args, {
|
|
40
|
-
cwd: root,
|
|
41
|
-
stdio: "inherit"
|
|
42
|
-
});
|
|
43
|
-
const duration = formatDuration(performance.now() - started);
|
|
44
|
-
|
|
45
|
-
if (result.error) {
|
|
46
|
-
console.error(`<== ${name} FAILED (${duration})`);
|
|
47
|
-
console.error(result.error.message);
|
|
48
|
-
process.exit(1);
|
|
49
|
-
}
|
|
50
|
-
if (result.status !== 0) {
|
|
51
|
-
console.error(`<== ${name} FAILED (${duration})`);
|
|
52
|
-
process.exit(result.status ?? 1);
|
|
53
|
-
}
|
|
54
|
-
console.log(`<== ${name} passed (${duration})`);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
console.log(`\nAll ARM64 tests passed (${formatDuration(performance.now() - suiteStarted)})`);
|
|
@@ -1,229 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import os from "node:os";
|
|
4
|
-
import { execFileSync, spawnSync } from "node:child_process";
|
|
5
|
-
import { fileURLToPath } from "node:url";
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
decodeObject,
|
|
9
|
-
decodeWire,
|
|
10
|
-
objectToKVMArtifact,
|
|
11
|
-
specializeKVM
|
|
12
|
-
} from "../../../backend-api.mjs";
|
|
13
|
-
import { compileLibrary, compileObjectBuffer, loadLibrary } from "../../../object.mjs";
|
|
14
|
-
|
|
15
|
-
import {
|
|
16
|
-
compileKVMModuleToARM64,
|
|
17
|
-
emitMetadataC,
|
|
18
|
-
getTagEntries,
|
|
19
|
-
resetTagIds
|
|
20
|
-
} from "./kvm2arm64.mjs";
|
|
21
|
-
|
|
22
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
23
|
-
const RUNTIME_C_PATH = path.resolve(__dirname, "runtime.c");
|
|
24
|
-
const KRT_C_PATH = path.resolve(__dirname, "../../llvm/runtime/krt.c");
|
|
25
|
-
const KRT_H_DIR = path.resolve(__dirname, "../../llvm/runtime");
|
|
26
|
-
|
|
27
|
-
export const ARTIFACT_FORMAT = "k-arm64";
|
|
28
|
-
export const ARTIFACT_VERSION = 1;
|
|
29
|
-
|
|
30
|
-
function buildExportPreamble(exports = [], libraries = []) {
|
|
31
|
-
if (exports.length === 0) return "";
|
|
32
|
-
const aliasMap = {};
|
|
33
|
-
for (const lib of libraries) {
|
|
34
|
-
for (const [name, hash] of Object.entries(lib.relAlias || {})) {
|
|
35
|
-
if (name !== "__main__") aliasMap[name] = hash;
|
|
36
|
-
}
|
|
37
|
-
for (const [hash, entry] of Object.entries(lib.meta || {})) {
|
|
38
|
-
if (entry?.type !== "rel") continue;
|
|
39
|
-
for (const origin of entry?.origins || []) {
|
|
40
|
-
if (origin?.name && origin.name !== "__main__") {
|
|
41
|
-
aliasMap[origin.name] = hash;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
const lines = [];
|
|
47
|
-
for (const spec of exports) {
|
|
48
|
-
const [libName, localName] = spec.includes(":") ? spec.split(":", 2) : [spec, spec];
|
|
49
|
-
const hash = aliasMap[libName];
|
|
50
|
-
if (!hash) throw new Error(`--export: '${libName}' not found in loaded libraries`);
|
|
51
|
-
const body = hash.startsWith("@") ? hash.slice(1) : hash;
|
|
52
|
-
lines.push(`${localName} = @${body};`);
|
|
53
|
-
}
|
|
54
|
-
return lines.join("\n") + "\n";
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export function compileARM64ArtifactFromKVM(kvmInput, options = {}) {
|
|
58
|
-
let kvm = kvmInput;
|
|
59
|
-
if (kvm?.format === "k-vm") {
|
|
60
|
-
if (kvm.layer === "KVM-P" && options.inputEnvelopePattern) {
|
|
61
|
-
kvm = specializeKVM(kvm, options.inputEnvelopePattern);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const functions = kvm.functions || (typeof kvm === "object" && !Array.isArray(kvm) ? kvm : null);
|
|
66
|
-
if (!functions || typeof functions !== "object") {
|
|
67
|
-
throw new Error("Expected .kvm input to contain a functions object");
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const entry = options.entry || kvm.entry || "__main__";
|
|
71
|
-
const entryFunc = functions[entry];
|
|
72
|
-
if (!entryFunc) {
|
|
73
|
-
throw new Error(`kVM entry relation '${entry}' not found in program`);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const inputPattern = options.inputPattern || entryFunc.inputPattern || [];
|
|
77
|
-
const outputPattern = options.outputPattern || entryFunc.outputPattern || [];
|
|
78
|
-
|
|
79
|
-
resetTagIds();
|
|
80
|
-
const profile = Boolean(options.profile ?? (process.env.K_PROFILE === "1" || process.env.PROFILE === "1"));
|
|
81
|
-
const { assembly, entryName, profileFunctions } = compileKVMModuleToARM64(entry, functions, {
|
|
82
|
-
inputPattern,
|
|
83
|
-
outputPattern,
|
|
84
|
-
profile
|
|
85
|
-
});
|
|
86
|
-
const tags = getTagEntries();
|
|
87
|
-
const metadataC = emitMetadataC(tags, inputPattern, outputPattern, profileFunctions);
|
|
88
|
-
|
|
89
|
-
if (options.emit === "assembly" || options.assembly) {
|
|
90
|
-
return {
|
|
91
|
-
assembly,
|
|
92
|
-
metadataC,
|
|
93
|
-
entryName,
|
|
94
|
-
tags,
|
|
95
|
-
inputPattern,
|
|
96
|
-
outputPattern
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// Compile to native ELF executable using gcc
|
|
101
|
-
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "k-arm64-"));
|
|
102
|
-
const asmPath = path.join(tmpDir, "prog.s");
|
|
103
|
-
const metaPath = path.join(tmpDir, "meta.c");
|
|
104
|
-
const outPath = options.outputPath || path.join(tmpDir, "prog");
|
|
105
|
-
|
|
106
|
-
try {
|
|
107
|
-
fs.writeFileSync(asmPath, assembly, "utf8");
|
|
108
|
-
fs.writeFileSync(metaPath, metadataC, "utf8");
|
|
109
|
-
|
|
110
|
-
const gcc = options.gcc || process.env.CC || "gcc";
|
|
111
|
-
const optLevel = options.optLevel ?? "-O2";
|
|
112
|
-
const args = [
|
|
113
|
-
optLevel,
|
|
114
|
-
`-DENTRY_NAME=${entryName}`,
|
|
115
|
-
asmPath,
|
|
116
|
-
metaPath,
|
|
117
|
-
RUNTIME_C_PATH,
|
|
118
|
-
KRT_C_PATH,
|
|
119
|
-
`-I${KRT_H_DIR}`,
|
|
120
|
-
"-o",
|
|
121
|
-
outPath
|
|
122
|
-
];
|
|
123
|
-
|
|
124
|
-
execFileSync(gcc, args, { stdio: "pipe" });
|
|
125
|
-
|
|
126
|
-
if (options.outputPath) {
|
|
127
|
-
return outPath;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
const binaryBuffer = fs.readFileSync(outPath);
|
|
131
|
-
return binaryBuffer;
|
|
132
|
-
} finally {
|
|
133
|
-
try {
|
|
134
|
-
if (fs.existsSync(asmPath)) fs.unlinkSync(asmPath);
|
|
135
|
-
if (fs.existsSync(metaPath)) fs.unlinkSync(metaPath);
|
|
136
|
-
if (!options.outputPath && fs.existsSync(outPath)) fs.unlinkSync(outPath);
|
|
137
|
-
if (!options.outputPath) fs.rmdirSync(tmpDir);
|
|
138
|
-
} catch {
|
|
139
|
-
// Ignore cleanup errors in temporary directory
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
export function compileARM64ArtifactFromObject(object, options = {}) {
|
|
145
|
-
const kvm = objectToKVMArtifact(object, options.entry, null, options);
|
|
146
|
-
return compileARM64ArtifactFromKVM(kvm, options);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
export function compileARM64Artifact(source, options = {}) {
|
|
150
|
-
const libraries = (options.libraries || []).map((lib) => {
|
|
151
|
-
if (lib?.rels || lib?.meta || lib?.relAlias) return lib;
|
|
152
|
-
if (typeof lib === "string") {
|
|
153
|
-
const ext = path.extname(lib).toLowerCase();
|
|
154
|
-
if (ext === ".k") {
|
|
155
|
-
return loadLibrary(compileLibrary(fs.readFileSync(lib, "utf8"), { source: lib }));
|
|
156
|
-
}
|
|
157
|
-
return loadLibrary(decodeObject(fs.readFileSync(lib)));
|
|
158
|
-
}
|
|
159
|
-
if (Buffer.isBuffer(lib)) {
|
|
160
|
-
return loadLibrary(decodeObject(lib));
|
|
161
|
-
}
|
|
162
|
-
return lib;
|
|
163
|
-
});
|
|
164
|
-
const preamble = buildExportPreamble(options.exports || options.exportSpecs || [], libraries);
|
|
165
|
-
const fullSource = preamble + source;
|
|
166
|
-
|
|
167
|
-
const objBuffer = compileObjectBuffer(fullSource, {
|
|
168
|
-
libraries,
|
|
169
|
-
source: options.source || null
|
|
170
|
-
});
|
|
171
|
-
const object = decodeObject(objBuffer);
|
|
172
|
-
return compileARM64ArtifactFromObject(object, options);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
export function runARM64Artifact(executablePathOrBuffer, inputBuffer, options = {}) {
|
|
176
|
-
let execPath = executablePathOrBuffer;
|
|
177
|
-
let tmpFile = null;
|
|
178
|
-
|
|
179
|
-
if (Buffer.isBuffer(executablePathOrBuffer)) {
|
|
180
|
-
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "k-arm64-run-"));
|
|
181
|
-
tmpFile = path.join(tmpDir, "k-exec");
|
|
182
|
-
fs.writeFileSync(tmpFile, executablePathOrBuffer, { mode: 0o755 });
|
|
183
|
-
execPath = tmpFile;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
try {
|
|
187
|
-
const args = [];
|
|
188
|
-
if (options.json) {
|
|
189
|
-
args.push("--json");
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
const result = spawnSync(execPath, args, {
|
|
193
|
-
input: inputBuffer,
|
|
194
|
-
maxBuffer: options.maxBuffer || 64 * 1024 * 1024
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
if (result.error) {
|
|
198
|
-
throw result.error;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
if (result.status !== 0) {
|
|
202
|
-
const stderr = result.stderr?.toString("utf8") || "";
|
|
203
|
-
throw new Error(`ARM64 executable failed with exit status ${result.status}: ${stderr}`);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
if (options.json) {
|
|
207
|
-
return result.stdout.toString("utf8");
|
|
208
|
-
}
|
|
209
|
-
return result.stdout;
|
|
210
|
-
} finally {
|
|
211
|
-
if (tmpFile) {
|
|
212
|
-
try {
|
|
213
|
-
fs.unlinkSync(tmpFile);
|
|
214
|
-
fs.rmdirSync(path.dirname(tmpFile));
|
|
215
|
-
} catch {
|
|
216
|
-
// Ignore temporary cleanup errors
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
export default {
|
|
223
|
-
ARTIFACT_FORMAT,
|
|
224
|
-
ARTIFACT_VERSION,
|
|
225
|
-
compileARM64ArtifactFromKVM,
|
|
226
|
-
compileARM64ArtifactFromObject,
|
|
227
|
-
compileARM64Artifact,
|
|
228
|
-
runARM64Artifact
|
|
229
|
-
};
|
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
|
|
4
|
-
import { compileLibrary, decodeObject, loadLibrary } from "../../../backend-api.mjs";
|
|
5
|
-
import {
|
|
6
|
-
compileARM64Artifact,
|
|
7
|
-
compileARM64ArtifactFromKVM,
|
|
8
|
-
compileARM64ArtifactFromObject
|
|
9
|
-
} from "./arm64.mjs";
|
|
10
|
-
|
|
11
|
-
export function readAll(stream) {
|
|
12
|
-
return new Promise((resolve, reject) => {
|
|
13
|
-
const chunks = [];
|
|
14
|
-
stream.on("data", (chunk) => chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)));
|
|
15
|
-
stream.on("end", () => resolve(Buffer.concat(chunks)));
|
|
16
|
-
stream.on("error", reject);
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function parseCompileOptions(args) {
|
|
21
|
-
const libraries = [];
|
|
22
|
-
const exportSpecs = [];
|
|
23
|
-
let loadedLibrary = false;
|
|
24
|
-
|
|
25
|
-
while (args.length > 0) {
|
|
26
|
-
if (args[0] === "--") {
|
|
27
|
-
args.shift();
|
|
28
|
-
break;
|
|
29
|
-
}
|
|
30
|
-
if (args[0] === "--lib") {
|
|
31
|
-
if (loadedLibrary) throw new Error("--lib may only be specified once");
|
|
32
|
-
args.shift();
|
|
33
|
-
const libPath = args.shift();
|
|
34
|
-
if (!libPath) throw new Error("--lib requires a file argument");
|
|
35
|
-
const ext = path.extname(libPath).toLowerCase();
|
|
36
|
-
if (ext === ".k") {
|
|
37
|
-
libraries.push(loadLibrary(compileLibrary(fs.readFileSync(libPath, "utf8"), { source: libPath })));
|
|
38
|
-
} else {
|
|
39
|
-
libraries.push(loadLibrary(decodeObject(fs.readFileSync(libPath))));
|
|
40
|
-
}
|
|
41
|
-
loadedLibrary = true;
|
|
42
|
-
} else if (args[0] === "--export") {
|
|
43
|
-
args.shift();
|
|
44
|
-
const spec = args.shift();
|
|
45
|
-
if (!spec) throw new Error("--export requires a spec argument (name or libname:localname)");
|
|
46
|
-
exportSpecs.push(spec);
|
|
47
|
-
} else if (args[0].startsWith("--")) {
|
|
48
|
-
break;
|
|
49
|
-
} else {
|
|
50
|
-
break;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return { libraries, exportSpecs };
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export function fileInputType(filePath) {
|
|
58
|
-
const ext = path.extname(filePath).toLowerCase();
|
|
59
|
-
if (ext === ".ko") return "ko";
|
|
60
|
-
if (ext === ".kvm") return "kvm";
|
|
61
|
-
if (ext === ".klib") return "klib";
|
|
62
|
-
return "source-file";
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export function resolveProgramInput(args, { allowStdinSource = false } = {}) {
|
|
66
|
-
const inputArg = args.shift() ?? null;
|
|
67
|
-
if (inputArg == null) {
|
|
68
|
-
if (!allowStdinSource) throw new Error("Missing script argument");
|
|
69
|
-
return { kind: "stdin-source", sourceName: "<stdin>" };
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const ext = path.extname(inputArg).toLowerCase();
|
|
73
|
-
if (fs.existsSync(inputArg)) {
|
|
74
|
-
const kind = fileInputType(inputArg);
|
|
75
|
-
if (kind === "klib") {
|
|
76
|
-
throw new Error("Cannot compile a .klib library to ARM64 without a main relation; load it with --lib.");
|
|
77
|
-
}
|
|
78
|
-
return { kind, path: inputArg, sourceName: inputArg };
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
if ([".k", ".ko", ".kvm", ".klib"].includes(ext)) {
|
|
82
|
-
throw new Error(`Input file not found: ${inputArg}`);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return { kind: "inline-source", source: inputArg, sourceName: "<inline>" };
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export async function compileProgramInput(
|
|
89
|
-
input,
|
|
90
|
-
{
|
|
91
|
-
libraries = [],
|
|
92
|
-
exportSpecs = [],
|
|
93
|
-
stdin = null,
|
|
94
|
-
inputEnvelopePattern = null,
|
|
95
|
-
entry = null,
|
|
96
|
-
emit = null,
|
|
97
|
-
outputPath = null
|
|
98
|
-
} = {}
|
|
99
|
-
) {
|
|
100
|
-
const compileOptions = {
|
|
101
|
-
libraries,
|
|
102
|
-
exports: exportSpecs,
|
|
103
|
-
inputEnvelopePattern,
|
|
104
|
-
entry,
|
|
105
|
-
emit,
|
|
106
|
-
outputPath
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
if (input.kind === "inline-source") {
|
|
110
|
-
return compileARM64Artifact(input.source, {
|
|
111
|
-
...compileOptions,
|
|
112
|
-
source: input.sourceName
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
if (input.kind === "stdin-source") {
|
|
117
|
-
if (!stdin) throw new Error("stdin source requested without a stdin stream");
|
|
118
|
-
const source = (await readAll(stdin)).toString("utf8");
|
|
119
|
-
return compileARM64Artifact(source, {
|
|
120
|
-
...compileOptions,
|
|
121
|
-
source: input.sourceName
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
if (input.kind === "source-file") {
|
|
126
|
-
return compileARM64Artifact(fs.readFileSync(input.path, "utf8"), {
|
|
127
|
-
...compileOptions,
|
|
128
|
-
source: input.sourceName
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
if (input.kind === "ko") {
|
|
133
|
-
return compileARM64ArtifactFromObject(
|
|
134
|
-
decodeObject(fs.readFileSync(input.path)),
|
|
135
|
-
compileOptions
|
|
136
|
-
);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
if (input.kind === "kvm") {
|
|
140
|
-
return compileARM64ArtifactFromKVM(
|
|
141
|
-
JSON.parse(fs.readFileSync(input.path, "utf8")),
|
|
142
|
-
compileOptions
|
|
143
|
-
);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
throw new Error(`Unsupported input kind: ${input.kind}`);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
export default {
|
|
150
|
-
compileProgramInput,
|
|
151
|
-
fileInputType,
|
|
152
|
-
parseCompileOptions,
|
|
153
|
-
readAll,
|
|
154
|
-
resolveProgramInput
|
|
155
|
-
};
|