Compare commits

..

9 commits
trunk ... trunk

140 changed files with 8099 additions and 12769 deletions

2
.gitignore vendored
View file

@ -9,7 +9,5 @@ db.sqlite-journal
# assets
/depell/src/*.gz
/depell/src/*.wasm
/depell/src/static-pages/*.html
#**/*-sv.rs
/bytecode/src/instrs.rs
/lang/src/testcases.rs

112
Cargo.lock generated
View file

@ -39,10 +39,16 @@ dependencies = [
]
[[package]]
name = "anyhow"
version = "1.0.89"
name = "allocator-api2"
version = "0.2.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6"
checksum = "45862d1c77f2228b9e10bc609d5bc203d86ebc9b87ad8d5d5167a6c9abf739d9"
[[package]]
name = "anyhow"
version = "1.0.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775"
[[package]]
name = "arc-swap"
@ -229,7 +235,7 @@ dependencies = [
"proc-macro2",
"quote",
"regex",
"rustc-hash",
"rustc-hash 1.1.0",
"shlex",
"syn",
"which",
@ -259,6 +265,15 @@ dependencies = [
"generic-array",
]
[[package]]
name = "bumpalo"
version = "3.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
dependencies = [
"allocator-api2",
]
[[package]]
name = "bytes"
version = "1.8.0"
@ -267,9 +282,9 @@ checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da"
[[package]]
name = "cc"
version = "1.1.36"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baee610e9452a8f6f0a1b6194ec09ff9e2d85dea54432acdae41aa0761c95d70"
checksum = "1aeb932158bd710538c73702db6945cb68a8fb08c519e6e12706b94263b36db8"
dependencies = [
"jobserver",
"libc",
@ -333,9 +348,9 @@ dependencies = [
[[package]]
name = "cpufeatures"
version = "0.2.14"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0"
checksum = "0ca741a962e1b0bff6d724a1a0958b686406e853bb14061f218562e1896f95e6"
dependencies = [
"libc",
]
@ -576,9 +591,12 @@ dependencies = [
[[package]]
name = "hashbrown"
version = "0.15.0"
version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb"
checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3"
dependencies = [
"allocator-api2",
]
[[package]]
name = "hashlink"
@ -597,10 +615,11 @@ version = "0.1.0"
name = "hblang"
version = "0.1.0"
dependencies = [
"hashbrown 0.15.0",
"hashbrown 0.15.1",
"hbbytecode",
"hbvm",
"log",
"regalloc2",
]
[[package]]
@ -759,7 +778,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da"
dependencies = [
"equivalent",
"hashbrown 0.15.0",
"hashbrown 0.15.1",
"serde",
]
@ -807,9 +826,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
[[package]]
name = "libc"
version = "0.2.159"
version = "0.2.162"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5"
checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398"
[[package]]
name = "libloading"
@ -844,15 +863,6 @@ version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
[[package]]
name = "markdown"
version = "1.0.0-alpha.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6491e6c702bf7e3b24e769d800746d5f2c06a6c6a2db7992612e0f429029e81"
dependencies = [
"unicode-id",
]
[[package]]
name = "matchit"
version = "0.7.3"
@ -1013,9 +1023,9 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]]
name = "prettyplease"
version = "0.2.22"
version = "0.2.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba"
checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033"
dependencies = [
"proc-macro2",
"syn",
@ -1023,9 +1033,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.87"
version = "1.0.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a"
checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
dependencies = [
"unicode-ident",
]
@ -1048,6 +1058,19 @@ dependencies = [
"getrandom",
]
[[package]]
name = "regalloc2"
version = "0.10.2"
source = "git+https://github.com/jakubDoka/regalloc2?branch=reuse-allocations#21c43e3ee182824e92e2b25f1d3c03ed47f9c02b"
dependencies = [
"allocator-api2",
"bumpalo",
"hashbrown 0.14.5",
"log",
"rustc-hash 2.0.0",
"smallvec",
]
[[package]]
name = "regex"
version = "1.11.1"
@ -1062,9 +1085,9 @@ dependencies = [
[[package]]
name = "regex-automata"
version = "0.4.8"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3"
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
dependencies = [
"aho-corasick",
"memchr",
@ -1119,10 +1142,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustix"
version = "0.38.37"
name = "rustc-hash"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811"
checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152"
[[package]]
name = "rustix"
version = "0.38.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0"
dependencies = [
"bitflags",
"errno",
@ -1192,18 +1221,18 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
[[package]]
name = "serde"
version = "1.0.210"
version = "1.0.215"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.210"
version = "1.0.215"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0"
dependencies = [
"proc-macro2",
"quote",
@ -1295,9 +1324,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "syn"
version = "2.0.79"
version = "2.0.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590"
checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d"
dependencies = [
"proc-macro2",
"quote",
@ -1455,12 +1484,6 @@ version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "unicode-id"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10103c57044730945224467c09f71a4db0071c123a0648cc3e818913bde6b561"
[[package]]
name = "unicode-ident"
version = "1.0.13"
@ -1663,7 +1686,6 @@ name = "xtask"
version = "0.1.0"
dependencies = [
"anyhow",
"markdown",
"walrus",
]

View file

@ -17,7 +17,9 @@ members = [
[workspace.dependencies]
hbbytecode = { path = "bytecode", default-features = false }
hbvm = { path = "vm", default-features = false }
hbxrt = { path = "xrt" }
hblang = { path = "lang", default-features = false }
hbjit = { path = "jit" }
[profile.release]
lto = true

View file

@ -5,6 +5,6 @@ edition = "2018"
[features]
default = ["disasm"]
disasm = ["alloc"]
alloc = []
std = []
disasm = ["std"]

View file

@ -98,27 +98,6 @@ fn gen_instrs(generated: &mut String) -> Result<(), Box<dyn std::error::Error>>
writeln!(generated, " {name} = {id},")?;
}
writeln!(generated, "}}")?;
writeln!(generated, "impl {instr} {{")?;
writeln!(generated, " pub fn size(self) -> usize {{")?;
writeln!(generated, " match self {{")?;
let mut instrs = instructions().collect::<Vec<_>>();
instrs.sort_unstable_by_key(|&[.., ty, _]| iter_args(ty).map(arg_to_width).sum::<usize>());
for group in instrs.chunk_by(|[.., a, _], [.., b, _]| {
iter_args(a).map(arg_to_width).sum::<usize>()
== iter_args(b).map(arg_to_width).sum::<usize>()
}) {
let ty = group[0][2];
for &[_, name, ..] in group {
writeln!(generated, " | {instr}::{name}")?;
}
generated.pop();
let size = iter_args(ty).map(arg_to_width).sum::<usize>() + 1;
writeln!(generated, " => {size},")?;
}
writeln!(generated, " }}")?;
writeln!(generated, " }}")?;
writeln!(generated, "}}")?;
}
'_arg_kind: {

View file

@ -254,7 +254,8 @@ pub fn disasm<'a>(
|| global_offset > off + len
|| prev
.get(global_offset as usize)
.is_none_or(|&b| instr_from_byte(b).is_err());
.map_or(true, |&b| instr_from_byte(b).is_err())
|| prev[global_offset as usize] == 0;
has_oob |= local_has_oob;
let label = labels.get(&global_offset).unwrap();
if local_has_oob {

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="M480-320 280-520l56-58 104 104v-326h80v326l104-104 56 58-200 200ZM240-160q-33 0-56.5-23.5T160-240v-120h80v120h480v-120h80v120q0 33-23.5 56.5T720-160H240Z"/></svg>

Before

Width:  |  Height:  |  Size: 279 B

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="M320-200v-560l440 280-440 280Zm80-280Zm0 134 210-134-210-134v268Z"/></svg>

Before

Width:  |  Height:  |  Size: 190 B

View file

@ -1,15 +1,16 @@
* {
font-family: var(--font);
line-height: 1.3;
}
body {
--primary: light-dark(white, #181A1B);
--secondary: light-dark(#EFEFEF, #212425);
--timestamp: light-dark(#555555, #AAAAAA);
--primary: white;
--secondary: #EFEFEF;
--timestamp: #777777;
--error: #ff3333;
--placeholder: #333333;
}
body {
--small-gap: 5px;
--font: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
@ -35,11 +36,6 @@ body {
}
div.preview {
margin: var(--small-gap) 0px;
display: flex;
flex-direction: column;
gap: var(--small-gap);
div.info {
display: flex;
gap: var(--small-gap);
@ -49,32 +45,10 @@ div.preview {
}
}
div.stat {
div.stats {
display: flex;
svg {
height: 18px;
}
gap: var(--small-gap);
}
div.code {
position: relative;
nav {
position: absolute;
right: 0;
padding: var(--small-gap);
button {
display: flex;
padding: 0;
}
}
}
}
svg {
fill: black;
}
form {
@ -109,8 +83,6 @@ pre {
font-family: var(--monospace);
tab-size: 4;
overflow-x: auto;
white-space: pre-wrap;
word-wrap: break-word;
}
input {
@ -136,11 +108,6 @@ button:hover:not(:active) {
background: var(--primary);
}
code {
font-family: var(--monospace);
line-height: 1;
}
div#code-editor {
display: flex;
position: relative;
@ -174,40 +141,3 @@ div#dep-list {
}
}
}
.syn {
font-family: var(--monospace);
&.Comment {
color: #939f91;
}
&.Keyword {
color: #f85552;
}
&.Identifier,
&.Directive {
color: #3a94c5;
}
/* &.Number {} */
&.String {
color: #8da101;
}
&.Op,
&.Assign {
color: #f57d26;
}
&.Paren,
&.Bracket,
&.Comma,
&.Dot,
&.Ctor,
&.Colon {
color: light-dark(#5c6a72, #999999);
}
}

View file

@ -14,7 +14,7 @@ const stack_pointer_offset = 1 << 20;
/** @param {WebAssembly.Instance} instance @param {Post[]} packages @param {number} fuel
* @returns {string} */
function compileCode(instance, packages, fuel = 100) {
function compileCode(instance, packages, fuel) {
let {
INPUT, INPUT_LEN,
LOG_MESSAGES, LOG_MESSAGES_LEN,
@ -34,7 +34,7 @@ function compileCode(instance, packages, fuel = 100) {
new DataView(memory.buffer).setUint32(INPUT_LEN.value, codeLength, true);
runWasmFunction(instance, compile_and_run, fuel);
return bufToString(memory, LOG_MESSAGES, LOG_MESSAGES_LEN).trim();
return bufToString(memory, LOG_MESSAGES, LOG_MESSAGES_LEN);
}
/**@type{WebAssembly.Instance}*/ let fmtInstance;
@ -44,25 +44,23 @@ async function getFmtInstance() {
return fmtInstance ??= (await fmtInstaceFuture).instance;
}
/** @param {WebAssembly.Instance} instance @param {string} code @param {"tok" | "fmt" | "minify"} action
* @returns {string | Uint8Array | undefined} */
/** @param {WebAssembly.Instance} instance @param {string} code @param {"fmt" | "minify"} action
* @returns {string | undefined} */
function modifyCode(instance, code, action) {
let {
INPUT, INPUT_LEN,
OUTPUT, OUTPUT_LEN,
memory, fmt, tok, minify
memory, fmt, minify
} = instance.exports;
let funs = { fmt, tok, minify };
let fun = funs[action];
if (!(true
&& memory instanceof WebAssembly.Memory
&& INPUT instanceof WebAssembly.Global
&& INPUT_LEN instanceof WebAssembly.Global
&& OUTPUT instanceof WebAssembly.Global
&& OUTPUT_LEN instanceof WebAssembly.Global
&& funs.hasOwnProperty(action)
&& typeof fun === "function"
&& typeof fmt === "function"
&& typeof minify === "function"
)) never();
if (action !== "fmt") {
@ -74,14 +72,8 @@ function modifyCode(instance, code, action) {
dw.setUint32(INPUT_LEN.value, code.length, true);
new Uint8Array(memory.buffer, INPUT.value).set(new TextEncoder().encode(code));
if (!runWasmFunction(instance, fun)) {
return undefined;
}
if (action === "tok") {
return bufSlice(memory, OUTPUT, OUTPUT_LEN);
} else {
return bufToString(memory, OUTPUT, OUTPUT_LEN);
}
return runWasmFunction(instance, action === "fmt" ? fmt : minify) ?
bufToString(memory, OUTPUT, OUTPUT_LEN) : undefined;
}
@ -127,15 +119,6 @@ function packPosts(posts, view) {
return len;
}
/** @param {WebAssembly.Memory} mem
* @param {WebAssembly.Global} ptr
* @param {WebAssembly.Global} len
* @return {Uint8Array} */
function bufSlice(mem, ptr, len) {
return new Uint8Array(mem.buffer, ptr.value,
new DataView(mem.buffer).getUint32(len.value, true));
}
/** @param {WebAssembly.Memory} mem
* @param {WebAssembly.Global} ptr
* @param {WebAssembly.Global} len
@ -158,13 +141,12 @@ function wireUp(target) {
const importRe = /@use\s*\(\s*"(([^"]|\\")+)"\s*\)/g;
/** @param {WebAssembly.Instance} fmt
* @param {string} code
/** @param {string} code
* @param {string[]} roots
* @param {Post[]} buf
* @param {Set<string>} prevRoots
* @returns {void} */
function loadCachedPackages(fmt, code, roots, buf, prevRoots) {
function loadCachedPackages(code, roots, buf, prevRoots) {
buf[0].code = code;
roots.length = 0;
@ -180,10 +162,7 @@ function loadCachedPackages(fmt, code, roots, buf, prevRoots) {
for (let imp = roots.pop(); imp !== undefined; imp = roots.pop()) {
if (prevRoots.has(imp)) continue; prevRoots.add(imp);
const fmtd = modifyCode(fmt, localStorage.getItem("package-" + imp) ?? never(), "fmt");
if (typeof fmtd != "string") never();
buf.push({ path: imp, code: fmtd });
buf.push({ path: imp, code: localStorage.getItem("package-" + imp) ?? never() });
for (const match of buf[buf.length - 1].code.matchAll(importRe)) {
roots.push(match[1]);
}
@ -191,61 +170,6 @@ function loadCachedPackages(fmt, code, roots, buf, prevRoots) {
}
/**@type{Set<string>}*/ const prevRoots = new Set();
/**@typedef {Object} PackageCtx
* @property {AbortController} [cancelation]
* @property {string[]} keyBuf
* @property {Set<string>} prevParams
* @property {HTMLTextAreaElement} [edit] */
/** @param {string} source @param {Set<string>} importDiff @param {HTMLPreElement} errors @param {PackageCtx} ctx */
async function fetchPackages(source, importDiff, errors, ctx) {
importDiff.clear();
for (const match of source.matchAll(importRe)) {
if (localStorage["package-" + match[1]]) continue;
importDiff.add(match[1]);
}
if (importDiff.size !== 0 && (ctx.prevParams.size != importDiff.size
|| [...ctx.prevParams.keys()].every(e => importDiff.has(e)))) {
if (ctx.cancelation) ctx.cancelation.abort();
ctx.prevParams.clear();
ctx.prevParams = new Set([...importDiff]);
ctx.cancelation = new AbortController();
ctx.keyBuf.length = 0;
ctx.keyBuf.push(...importDiff.keys());
errors.textContent = "fetching: " + ctx.keyBuf.join(", ");
await fetch(`/code`, {
method: "POST",
signal: ctx.cancelation.signal,
headers: { "Content-Type": "application/json" },
body: JSON.stringify(ctx.keyBuf),
}).then(async e => {
try {
const json = await e.json();
if (e.status == 200) {
for (const [key, value] of Object.entries(json)) {
localStorage["package-" + key] = value;
}
const missing = ctx.keyBuf.filter(i => json[i] === undefined);
if (missing.length !== 0) {
errors.textContent = "deps not found: " + missing.join(", ");
} else {
ctx.cancelation = undefined;
ctx.edit?.dispatchEvent(new InputEvent("input"));
}
}
} catch (er) {
errors.textContent = "completely failed to fetch ("
+ e.status + "): " + ctx.keyBuf.join(", ");
console.error(e, er);
}
});
}
}
/** @param {HTMLElement} target */
async function bindCodeEdit(target) {
@ -264,29 +188,72 @@ async function bindCodeEdit(target) {
const hbc = await getHbcInstance(), fmt = await getFmtInstance();
let importDiff = new Set();
const keyBuf = [];
/**@type{Post[]}*/
const packages = [{ path: "local.hb", code: "" }];
const debounce = 100;
/**@type{AbortController|undefined}*/
let cancelation = undefined;
let timeout = 0;
const ctx = { keyBuf: [], prevParams: new Set(), edit };
prevRoots.clear();
const onInput = () => {
fetchPackages(edit.value, importDiff, errors, ctx);
importDiff.clear();
for (const match of edit.value.matchAll(importRe)) {
if (localStorage["package-" + match[1]]) continue;
importDiff.add(match[1]);
}
if (ctx.cancelation && importDiff.size !== 0) {
if (importDiff.size !== 0) {
if (cancelation) cancelation.abort();
cancelation = new AbortController();
keyBuf.length = 0;
keyBuf.push(...importDiff.keys());
errors.textContent = "fetching: " + keyBuf.join(", ");
fetch(`/code`, {
method: "POST",
signal: cancelation.signal,
headers: { "Content-Type": "application/json" },
body: JSON.stringify(keyBuf),
}).then(async e => {
try {
const json = await e.json();
if (e.status == 200) {
for (const [key, value] of Object.entries(json)) {
localStorage["package-" + key] = value;
}
const missing = keyBuf.filter(i => json[i] === undefined);
if (missing.length !== 0) {
errors.textContent = "deps not found: " + missing.join(", ");
} else {
cancelation = undefined;
edit.dispatchEvent(new InputEvent("input"));
}
}
} catch (er) {
errors.textContent = "completely failed to fetch ("
+ e.status + "): " + keyBuf.join(", ");
console.error(e, er);
}
});
}
if (cancelation && importDiff.size !== 0) {
return;
}
loadCachedPackages(fmt, edit.value, ctx.keyBuf, packages, prevRoots);
loadCachedPackages(edit.value, keyBuf, packages, prevRoots);
errors.textContent = compileCode(hbc, packages);
errors.textContent = compileCode(hbc, packages, 1);
const minified_size = modifyCode(fmt, edit.value, "minify")?.length;
if (minified_size) {
codeSize.textContent = (MAX_CODE_SIZE - minified_size) + "";
const perc = Math.min(100, Math.floor(100 * (minified_size / MAX_CODE_SIZE)));
codeSize.style.color = `color-mix(in srgb, light-dark(black, white), var(--error) ${perc}%)`;
codeSize.style.color = `color-mix(in srgb, white, var(--error) ${perc}%)`;
}
timeout = 0;
};
@ -298,86 +265,19 @@ async function bindCodeEdit(target) {
edit.dispatchEvent(new InputEvent("input"));
}
/**
* @type {Array<string>}
* to be synched with `enum TokenGroup` in bytecode/src/fmt.rs */
const TOK_CLASSES = [
'Blank',
'Comment',
'Keyword',
'Identifier',
'Directive',
'Number',
'String',
'Op',
'Assign',
'Paren',
'Bracket',
'Colon',
'Comma',
'Dot',
'Ctor',
];
/** @type {{ [key: string]: (el: HTMLElement) => void | Promise<void> }} */
/** @type {{ [key: string]: (content: string) => Promise<string> | string }} */
const applyFns = {
timestamp: (el) => {
const timestamp = el.innerText;
const date = new Date(parseInt(timestamp) * 1000);
el.innerText = date.toLocaleString();
},
fmt,
timestamp: (content) => new Date(parseInt(content) * 1000).toLocaleString(),
fmt: (content) => getFmtInstance().then(i => modifyCode(i, content, "fmt") ?? "invalid code"),
};
/**
* @param {HTMLElement} target */
async function fmt(target) {
const code = target.innerText;
const instance = await getFmtInstance();
const decoder = new TextDecoder('utf-8');
const fmt = modifyCode(instance, code, 'fmt');
if (typeof fmt !== "string") return;
const codeBytes = new TextEncoder().encode(fmt);
const tok = modifyCode(instance, fmt, 'tok');
if (!(tok instanceof Uint8Array)) return;
target.innerHTML = '';
let start = 0;
let kind = tok[0];
for (let ii = 1; ii <= tok.length; ii += 1) {
// split over same tokens and buffer end
if (tok[ii] === kind && ii < tok.length) {
continue;
}
const text = decoder.decode(codeBytes.subarray(start, ii));
const textNode = document.createTextNode(text);;
if (kind === 0) {
target.appendChild(textNode);
} else {
const el = document.createElement('span');
el.classList.add('syn');
el.classList.add(TOK_CLASSES[kind]);
el.appendChild(textNode);
target.appendChild(el);
}
if (ii == tok.length) {
break;
}
start = ii;
kind = tok[ii];
}
}
/** @param {HTMLElement} target */
function execApply(target) {
const proises = [];
for (const elem of target.querySelectorAll('[apply]')) {
if (!(elem instanceof HTMLElement)) continue;
const funcname = elem.getAttribute('apply') ?? never();
const vl = applyFns[funcname](elem);
if (vl instanceof Promise) proises.push(vl);
}
if (target === document.body) {
Promise.all(proises).then(() => document.body.hidden = false);
let res = applyFns[funcname](elem.textContent ?? "");
if (res instanceof Promise) res.then(c => elem.textContent = c);
else elem.textContent = res;
}
}
@ -432,13 +332,10 @@ function cacheInputs(target) {
}
/** @param {string} [path] */
function updateTab(path) {
console.log(path);
function updaetTab(path) {
for (const elem of document.querySelectorAll("button[hx-push-url]")) {
if (elem instanceof HTMLButtonElement)
elem.disabled =
elem.getAttribute("hx-push-url") === path
|| elem.getAttribute("hx-push-url") === window.location.pathname;
elem.disabled = elem.getAttribute("hx-push-url") === (path ?? window.location.pathname);
}
}
@ -454,7 +351,6 @@ if (window.location.hostname === 'localhost') {
const code = "main:=fn():void{return}";
const inst = await getFmtInstance()
const fmtd = modifyCode(inst, code, "fmt") ?? never();
if (typeof fmtd !== "string") never();
const prev = modifyCode(inst, fmtd, "minify") ?? never();
if (code != prev) console.error(code, prev);
}
@ -464,7 +360,7 @@ if (window.location.hostname === 'localhost') {
code: "main:=fn():int{return 42}",
}];
const res = compileCode(await getHbcInstance(), posts, 1) ?? never();
const expected = "exit code: 42";
const expected = "exit code: 42\n";
if (expected != res) console.error(expected, res);
}
})()
@ -474,7 +370,8 @@ document.body.addEventListener('htmx:afterSwap', (ev) => {
if (!(ev.target instanceof HTMLElement)) never();
wireUp(ev.target);
if (ev.target.tagName == "MAIN" || ev.target.tagName == "BODY")
updateTab(ev['detail'].pathInfo.finalRequestPath);
updaetTab(ev['detail'].pathInfo.finalRequestPath);
console.log(ev);
});
getFmtInstance().then(inst => {
@ -525,30 +422,6 @@ getFmtInstance().then(inst => {
Object.assign(window, { filterCodeDeps });
});
/** @param {HTMLElement} target */
function runPost(target) {
while (!target.matches("div[class=preview]")) target = target.parentElement ?? never();
const code = target.querySelector("pre[apply=fmt]");
if (!(code instanceof HTMLPreElement)) never();
const output = target.querySelector("pre[id=compiler-output]");
if (!(output instanceof HTMLPreElement)) never();
Promise.all([getHbcInstance(), getFmtInstance()]).then(async ([hbc, fmt]) => {
const ctx = { keyBuf: [], prevParams: new Set() };
await fetchPackages(code.innerText ?? never(), new Set(), output, ctx);
const posts = [{ path: "this", code: "" }];
loadCachedPackages(fmt, code.innerText ?? never(), ctx.keyBuf, posts, new Set());
output.textContent = compileCode(hbc, posts);
output.hidden = false;
});
let author = encodeURIComponent(target.dataset.author ?? never());
let name = encodeURIComponent(target.dataset.name ?? never());
fetch(`/post/run?author=${author}&name=${name}`, { method: "POST" })
}
Object.assign(window, { runPost });
updateTab();
updaetTab();
wireUp(document.body);

View file

@ -1,10 +1,10 @@
#![feature(iter_collect_into, macro_metavar_expr)]
#![feature(iter_collect_into)]
use {
argon2::{password_hash::SaltString, PasswordVerifier},
axum::{
body::Bytes,
extract::{DefaultBodyLimit, Path},
http::{header::COOKIE, request::Parts, StatusCode},
extract::Path,
http::{header::COOKIE, request::Parts},
response::{AppendHeaders, Html},
},
const_format::formatcp,
@ -52,27 +52,22 @@ async fn amain() {
db::init();
let router = axum::Router::new()
.route("/", get(Index::page))
.route("/index.css", static_asset!("text/css", "index.css"))
.route("/index.js", static_asset!("text/javascript", "index.js"))
.route("/hbfmt.wasm", static_asset!("application/wasm", "hbfmt.wasm"))
.route("/hbc.wasm", static_asset!("application/wasm", "hbc.wasm"))
.route("/", get(Index::page))
.route("/index-view", get(Index::get_with_blog))
.route("/blogs/index-view", get(Index::get))
.route("/blogs/developing-hblang", get(DevelopingHblang::page))
.route("/blogs/developing-hblang-view", get(DevelopingHblang::get))
.route("/index-view", get(Index::get))
.route("/feed", get(Feed::page))
.route("/feed-view", get(Feed::get))
.route("/feed-more", post(Feed::more))
.route("/profile", get(Profile::page))
.route("/profile-view", get(Profile::get))
.route("/profile/:name", get(Profile::get_other_page))
.route("/profile/password", post(PasswordChange::post))
.route("/profile-view/:name", get(Profile::get_other))
.route("/post", get(Post::page))
.route("/post-view", get(Post::get))
.route("/post", post(Post::post))
.route("/post/run", post(Post::run))
.route("/code", post(fetch_code))
.route("/login", get(Login::page))
.route("/login-view", get(Login::get))
@ -90,8 +85,7 @@ async fn amain() {
.as_millis();
move || async move { id.to_string() }
}),
)
.layer(DefaultBodyLimit::max(16 * 1024));
);
#[cfg(feature = "tls")]
{
@ -202,45 +196,12 @@ impl Page for Feed {
}
}
macro_rules! decl_static_pages {
($(
#[derive(PublicPage)]
#[page(static = $file:literal)]
struct $name:ident;
)*) => {
const ALL_STATIC_PAGES: [&str; ${count($file)}] = [$($file),*];
#[derive(Default)]
struct Index;
$(
#[derive(Default)]
struct $name;
impl PublicPage for $name {
fn render_to_buf(self, buf: &mut String) {
buf.push_str(include_str!(concat!("static-pages/", $file, ".html")));
}
async fn page(session: Option<Session>) -> Html<String> {
base(|s| blog_base(s, |s| Self::default().render_to_buf(s)), session.as_ref())
}
}
)*
};
}
decl_static_pages! {
#[derive(PublicPage)]
#[page(static = "welcome")]
struct Index;
#[derive(PublicPage)]
#[page(static = "developing-hblang")]
struct DevelopingHblang;
}
impl Index {
async fn get_with_blog() -> Html<String> {
let mut buf = String::new();
blog_base(&mut buf, |s| Index.render_to_buf(s));
Html(buf)
impl PublicPage for Index {
fn render_to_buf(self, buf: &mut String) {
buf.push_str(include_str!("welcome-page.html"));
}
}
@ -278,27 +239,11 @@ impl Page for Post {
<input type="submit" value="submit">
<pre id="compiler-output"></pre>
</form>
<div id="dep-list">
<input placeholder="search impoted deps.." oninput="filterCodeDeps(this, event)">
<section id="deps">
"results show here..."
</section>
</div>
<div>
!{include_str!("static-pages/post.html")}
</div>
!{include_str!("post-page.html")}
}
}
}
#[derive(Deserialize)]
struct Run {
author: String,
name: String,
}
impl Post {
pub fn from_row(r: &rusqlite::Row) -> rusqlite::Result<Self> {
Ok(Post {
@ -306,25 +251,10 @@ impl Post {
name: r.get(1)?,
timestamp: r.get(2)?,
code: r.get(3)?,
imports: r.get(4)?,
runs: r.get(5)?,
..Default::default()
})
}
async fn run(
session: Session,
axum::extract::Query(run): axum::extract::Query<Run>,
) -> StatusCode {
match db::with(|qes| qes.creata_run.insert((run.name, run.author, session.name))) {
Ok(_) => StatusCode::OK,
Err(e) => {
log::error!("creating run record failed: {e}");
StatusCode::INTERNAL_SERVER_ERROR
}
}
}
async fn post(
session: Session,
axum::Form(mut data): axum::Form<Self>,
@ -380,7 +310,7 @@ impl Post {
impl fmt::Display for Post {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { author, name, timestamp, imports, runs, dependencies, code, .. } = self;
write_html! { f <div class="preview" "data-author"=author "data-name"=name>
write_html! { f <div class="preview">
<div class="info">
<span>
<a "hx-get"={format_args!("/profile-view/{author}")} href="" "hx-target"="main"
@ -390,21 +320,16 @@ impl fmt::Display for Post {
name
</span>
<span apply="timestamp">timestamp</span>
for (name, count) in [include_str!("icons/download.svg"), include_str!("icons/run.svg"), "deps"]
.iter()
</div>
<div class="stats">
for (name, count) in "inps runs deps".split(' ')
.zip([imports, runs, dependencies])
.filter(|(_, &c)| c != 0)
{
<div class="stat">!name count</div>
name ": "<span>count</span>
}
</div>
<div class="code">
<nav>
<button onmousedown="runPost(this)">!{include_str!("icons/run.svg")}</button>
</nav>
<pre apply="fmt">code</pre>
</div>
<pre hidden id="compiler-output"></pre>
<pre apply="fmt">code</pre>
if *timestamp == 0 {
<button "hx-get"="/post" "hx-swap"="outerHTML"
"hx-target"="[preview]">"edit"</button>
@ -414,70 +339,6 @@ impl fmt::Display for Post {
}
}
#[derive(Deserialize, Default)]
struct PasswordChange {
old_password: String,
new_password: String,
#[serde(skip)]
error: Option<&'static str>,
}
impl PasswordChange {
async fn post(
session: Session,
axum::Form(mut change): axum::Form<PasswordChange>,
) -> Html<String> {
db::with(|que| {
match que.authenticate.query_row((&session.name,), |r| r.get::<_, String>(1)) {
Ok(hash) if verify_password(&hash, &change.old_password).is_err() => {
change.error = Some("invalid credentials");
}
Ok(_) => {
let new_hashed = hash_password(&change.new_password);
match que
.change_passowrd
.execute((new_hashed, &session.name))
.log("execute update")
{
None => change.error = Some("intenal server error"),
Some(0) => change.error = Some("password is incorrect"),
Some(_) => {}
}
}
Err(rusqlite::Error::QueryReturnedNoRows) => {
change.error = Some("invalid credentials");
}
Err(e) => {
log::error!("login queri failed: {e}");
change.error = Some("internal server error");
}
}
});
if change.error.is_some() {
change.render(&session)
} else {
PasswordChange::default().render(&session)
}
}
}
impl Page for PasswordChange {
fn render_to_buf(self, _: &Session, buf: &mut String) {
let Self { old_password, new_password, error } = self;
write_html! { (buf)
<form "hx-post"="/profile/password" "hx-swap"="outerHTML">
if let Some(e) = error { <div class="error">e</div> }
<input name="old_password" type="password" autocomplete="old-password"
placeholder="old password" value=old_password>
<input name="new_password" type="password" autocomplete="new-password" placeholder="new password"
value=new_password>
<input type="submit" value="submit">
</form>
}
}
}
#[derive(Default)]
struct Profile {
other: Option<String>,
@ -496,24 +357,20 @@ impl Profile {
impl Page for Profile {
fn render_to_buf(self, session: &Session, buf: &mut String) {
db::with(|db| {
let name = self.other.as_ref().unwrap_or(&session.name);
let iter = db
.get_user_posts
.query_map((name,), Post::from_row)
.query_map((self.other.as_ref().unwrap_or(&session.name),), Post::from_row)
.log("get user posts query")
.into_iter()
.flatten()
.filter_map(|p| p.log("user post row"));
write_html! { (*buf)
if name == &session.name {
|b|{PasswordChange::default().render_to_buf(session, b)}
}
write_html! { (buf)
for post in iter {
!{post}
} else {
"no posts"
}
!{include_str!("profile-page.html")}
}
})
}
@ -542,7 +399,7 @@ struct Login {
impl PublicPage for Login {
fn render_to_buf(self, buf: &mut String) {
let Self { name, password, error } = self;
let Login { name, password, error } = self;
write_html! { (buf)
<form "hx-post"="/login" "hx-swap"="outerHTML">
if let Some(e) = error { <div class="error">e</div> }
@ -582,12 +439,13 @@ impl Login {
data.error = Some("invalid credentials");
}
Err(e) => {
log::error!("login queri failed: {e}");
log::error!("foo {e}");
data.error = Some("internal server error");
}
});
if data.error.is_some() {
log::error!("what {:?}", data);
Err(data.render())
} else {
Ok(AppendHeaders([
@ -676,29 +534,6 @@ impl Signup {
}
}
fn blog_base(s: &mut String, body: impl FnOnce(&mut String)) {
let nav_button = |f: &mut String, name: &str| {
write_html! {(f)
<button "hx-push-url"={format_args!("/blogs/{name}")}
"hx-get"={format_args!("/blogs/{name}-view")}
"hx-target"="main#blog"
"hx-swap"="innerHTML">name</button>
}
};
write_html! {(*s)
<nav><section>
<button "hx-push-url"="/" "hx-get"="/blogs/index-view"
"hx-target"="main#blog" "hx-swap"="innerHTML">"welcome"</button>
for name in &ALL_STATIC_PAGES[1..] {
|f|{nav_button(f, name)}
}
</section></nav>
<section id="post-form"></section>
<main id="blog">|f|{body(f)}</main>
}
}
fn base(body: impl FnOnce(&mut String), session: Option<&Session>) -> Html<String> {
let username = session.map(|s| &s.name);
@ -716,17 +551,15 @@ fn base(body: impl FnOnce(&mut String), session: Option<&Session>) -> Html<Strin
<html lang="en">
<head>
<meta name="charset" content="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="code dependency hell socila media hblang">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="/index.css">
<title>"depell"</title>
</head>
<body hidden>
<body>
<nav>
<button "hx-push-url"="/" "hx-get"="/index-view" "hx-target"="main" "hx-swap"="innerHTML">"depell"</button>
<section>
if let Some(username) = username {
<button "hx-push-url"={format_args!("/profile/{username}")} "hx-get"="/profile-view" "hx-target"="main"
<button "hx-push-url"="/profile" "hx-get"="/profile-view" "hx-target"="main"
"hx-swap"="innerHTML">username</button>
|f|{nav_button(f, "feed"); nav_button(f, "post")}
<button "hx-delete"="/login">"logout"</button>
@ -849,58 +682,13 @@ mod db {
gen_queries! {
pub struct Queries {
register: "INSERT INTO user (name, password_hash) VALUES(?, ?)",
change_passowrd: "UPDATE user SET password_hash = ? WHERE name = ?",
authenticate: "SELECT name, password_hash FROM user WHERE name = ?",
login: "INSERT OR REPLACE INTO session (id, username, expiration) VALUES(?, ?, ?)",
logout: "DELETE FROM session WHERE id = ?",
get_session: "SELECT username, expiration FROM session WHERE id = ?",
get_user_posts: "SELECT author, name, timestamp, code, (
WITH RECURSIVE roots(name, author, code) AS (
SELECT name, author, code FROM post WHERE name = outher.name AND author = outher.author
UNION
SELECT post.name, post.author, post.code FROM
post JOIN import ON post.name = import.from_name
AND post.author = import.from_author
JOIN roots ON import.to_name = roots.name
AND import.to_author = roots.author
) SELECT (count(*) - 1) FROM roots
) AS imports, (
WITH RECURSIVE roots(name, author, code) AS (
SELECT name, author, code FROM post WHERE name = outher.name AND author = outher.author
UNION
SELECT post.name, post.author, post.code FROM post
JOIN import ON post.name = import.from_name
AND post.author = import.from_author
JOIN roots ON import.to_name = roots.name
AND import.to_author = roots.author
) SELECT count(*) FROM roots
JOIN run ON roots.name = run.code_name
AND roots.author = run.code_author
) AS runs FROM post as outher WHERE author = ? ORDER BY timestamp DESC",
// TODO: we might want to cache the recursive queries
get_pots_before: "SELECT author, name, timestamp, code, (
WITH RECURSIVE roots(name, author, code) AS (
SELECT name, author, code FROM post WHERE name = outher.name AND author = outher.author
UNION
SELECT post.name, post.author, post.code FROM
post JOIN import ON post.name = import.from_name
AND post.author = import.from_author
JOIN roots ON import.to_name = roots.name
AND import.to_author = roots.author
) SELECT (count(*) - 1) FROM roots
) AS imports, (
WITH RECURSIVE roots(name, author, code) AS (
SELECT name, author, code FROM post WHERE name = outher.name AND author = outher.author
UNION
SELECT post.name, post.author, post.code FROM post
JOIN import ON post.name = import.from_name
AND post.author = import.from_author
JOIN roots ON import.to_name = roots.name
AND import.to_author = roots.author
) SELECT count(*) FROM roots
JOIN run ON roots.name = run.code_name
AND roots.author = run.code_author
) as runs FROM post AS outher WHERE timestamp < ?",
get_user_posts: "SELECT author, name, timestamp, code FROM post WHERE author = ?
ORDER BY timestamp DESC",
get_pots_before: "SELECT author, name, timestamp, code FROM post WHERE timestamp < ?",
create_post: "INSERT INTO post (name, author, timestamp, code) VALUES(?, ?, ?, ?)",
fetch_deps: "
WITH RECURSIVE roots(name, author, code) AS (
@ -915,7 +703,6 @@ mod db {
",
create_import: "INSERT INTO import(to_author, to_name, from_author, from_name)
VALUES(?, ?, ?, ?)",
creata_run: "INSERT OR IGNORE INTO run(code_name, code_author, runner) VALUES(?, ?, ?)",
}
}
@ -942,22 +729,8 @@ mod db {
}
pub fn init() {
const SCHEMA_VERSION: usize = 0;
const MIGRATIONS: &[&str] = &[include_str!("migrations/1.sql")];
let db = rusqlite::Connection::open("db.sqlite").unwrap();
db.execute_batch(include_str!("schema.sql")).unwrap();
let schema_version =
db.pragma_query_value(None, "user_version", |v| v.get::<_, usize>(0)).unwrap();
if schema_version != SCHEMA_VERSION {
for &mig in &MIGRATIONS[schema_version..] {
db.execute_batch(mig).expect(mig);
}
db.pragma_update(None, "user_version", SCHEMA_VERSION).unwrap();
}
Queries::new(&db);
}
}

21
depell/src/post-page.html Normal file
View file

@ -0,0 +1,21 @@
<div id="dep-list">
<input placeholder="search impoted deps.." oninput="filterCodeDeps(this, event)">
<section id="deps">
results show here...
</section>
</div>
<div>
<h3>About posting code</h3>
<p>
If you are unfammiliar with <a href="https://git.ablecorp.us/AbleOS/holey-bytes">hblang</a>, refer to the
<strong>hblang/README.md</strong> or
vizit <a href="/profile/mlokis">mlokis'es posts</a>. Preferably don't edit the code here.
</p>
<h3>Extra textarea features</h3>
<ul>
<li>proper tab behaviour</li>
<li>snap to previous tab boundary on "empty" lines</li>
</ul>

View file

@ -1,61 +0,0 @@
# The journey to an optimizing compiler
It's been years since I was continuously trying to make a compiler to implement language of my dreams. Problem was tho that I wanted something similar to Rust, which if you did not know, `rustc` far exceeded the one million lines of code mark some time ago, so implementing such language would take me years if not decades, but I still tired it.
Besides being extremely ambitions, the problem with my earliest attempts at making a compiler, is that literally nobody, not even me, was using the language, and so retroactively I am confident, what I implemented was a complex test-case implementation, and not a compiler. I often fall into a trap of implementing edge cases instead of an algorithm that would handle not only the very few thing the tests do but also all the other stuff that users of the language would try.
Another part of why I was failing for all that time, is that I did the hardest thing first without understanding the core concepts involved in translating written language to IR, god forbid assembly. I wasted a lot of time like this, but at least I learned Rust well. At some point I found a job where I started developing a decentralized network and that fully drawn me away from language development.
## Completely new approach
At some point the company I was working for started having financial issues and they were unable to pay me. During that period, I discovered that my love for networking was majorly fueled by the monetary gains associated with it. I burned out, and started to look for things to do with the free time.
One could say timing was perfect because [`ableos`](https://git.ablecorp.us/AbleOS/ableos) was desperately in need of a sane programming language that compiles to the home made VM ISA used for all software ran in `ableos`, but there was nobody crazy enough to do this. I got terribly nerd sniped, tho I don't regret it. Process of making a language for `ableos` was completely different. Firstly, it needed to be done asap, the lack of a good language blocked everyone form writing drivers for `ableos`, secondly, the moment the language is at least a little bit usable, people other then me will start using it, and lastly, the ISA the language compiles to very simple to emit, understand, and run.
### Urgency is a bliss
I actually managed to make the language somewhat work in one week, mainly because my mind set changed. I no longer spent a lot of time designing syntax for elegance, I designed it so that it incredibly easy to parse, meaning I can spent minimal effort implementing the parser, and fully focus on the hard problem of translating AST to instructions. Surprisingly, making everything an expression and not enforcing any arbitrary rules, makes the code you can write incredibly flexible and (most) people love it. One of the decisions I made to save time (or maybe it was an accident) was to make `,;` not enforced, meaning, you are allowed to write delimiters in lists but, as long as it does not change the intent of the code, you can leave them out. In practice, you actually don't need semicolons, unless the next line starts with something sticky like `*x`, int that case you put a semicolon on the previous line to tell the parser where the current expression ends.
### Only the problem I care about
Its good to note that writing a parser is no longer interesting for me. I wrote many parsers before and writing one no longer feel rewarding, but more like a chore. The real problem I was excited about was translating AST to instructions, I always ended up overcomplicating this step wit edge cases for every possible scenario that can happen in code, for which there are infinite. But why did I succeed this time? Well all the friction related to getting something that I can execute was so low, I could iterate quickly and realize what I am doing wrong before I burn out. In a week I managed to understand what I was failing to do for years, partly because of all the previous suffering, but mainly because it was so easy to pivot and try new things. And so I managed to make my first single pass compiler, and people immediately started using it.
### Don't implement features nobody asked for
Immediately after someone else then me wrote something in `hb` stuff started breaking, over the course of a month I kept fixing bugs and adding new features just fine, and more people started to use the language. All was good and well until I looked into the code. It was incredibly cursed, full of tricks to work around the compiler not doing any optimizations. At that moment I realized the whole compiler after parser needs to be rewritten, I had to implement optimizations, otherwise people wont be able to write readable code that runs fast. All of the features I have added up until now, were a technical dept now. Unless they are all working with optimizations, can't compile the existing code. Yes, if feature exists, be sure as hell it will be used.
It took around 4 months to reimplement everything make make the optimal code look like what you are used to in other languages. I am really thankful for [sea of nodes](https://github.com/SeaOfNodes), and all the amazing work Cliff Click and others do to make demystify optimizers, It would have taken much longer to for me to figure all the principles out without the exhaustive [tutorial](https://github.com/SeaOfNodes/Simple?tab=readme-ov-file).
## How my understanding of optimizations changed
### Optimizations allow us to scale software
I need to admit, before writing a single pass compiler and later upgrading it to optimizing one, I thought optimizations only affect the quality of final assembly emitted by the compiler. It never occur to me that what the optimizations actually do, is reduce the impact of how you decide to write the code. In a single pass compiler (with zero optimizations), the machine code reflects:
- order of operations as written in code
- whether the value was stored in intermediate locations
- exact structure of the control flow and at which point the operations are placed
- how many times is something recomputed
- operations that only help to convey intent for the reader of the source code
- and more I can't think of...
If you took some code you wrote and then modified it to obfuscate these aspects (in reference to the original code), you would to a subset of what optimizing compiler does. Of course, a good compiler would try hard to improve the metrics its optimizing for, it would:
- reorder operations to allow the CPU to parallelize them
- remove needless stores, or store values directly to places you cant express in code
- pull operations out of the loops and into the branches (if it can)
- find all common sub-expressions and compute them only once
- fold constants as much as possible and use obscure tricks to replace slow instructions if any of the operands are constant
- and more...
In the end, compiler optimizations try to reduce correlation between how the code happens to be written and how well it performs, which is extremely important when you want humans to read the code.
### Optimizing compilers know more then you
Optimizing code is a search problem, an optimizer searches the code for patterns that can be rewritten so something more practical for the computer, while preserving the observable behavior of the program. This means it needs enough context about the code to not make a mistake. In fact, the optimizer has so much context, it is able to determine your code is useless. But wait, didn't you write the code because you needed it to do something? Maybe your intention was to break out of the loop after you are done, but the optimizer looked at the code and said, "great, we are so lucky that this integer is always small enough to miss this check by one, DELETE", and then he goes "jackpot, since this loop is now infinite, we don't need this code after it, DELETE". Notice that the optimizer is eager to delete dead code, it did not ask you "Brah, why did you place all your code after an infinite loop?". This is just an example, there are many more cases where modern optimizers just delete all your code because they proven it does something invalid without running it.
Its stupid but its the world we live in, optimizers are usually a black box you import and feed it the code in a format they understand, they then proceed to optimize it, and if they find a glaring bug they wont tell you, god forbid, they will just molest the code in unspecified ways and spit out whats left. Before writing an optimizer, I did no know this can happen and I did not know this is a problem I pay for with my time, spent figuring out why noting is happening when I run the program.
But wait its worse! Since optimizers wont ever share the fact you are stupid, we end up with other people painstakingly writing complex linters, that will do a shitty job detecting things that matter, and instead whine about style and other bullcrap (and they suck even at that). If the people who write linters and people who write optimizers swapped the roles, I would be ranting about optimizers instead.
And so, this is the area where I want to innovate, lets report the dead code to the frontend, and let the compiler frontend filter out the noise and show relevant information in the diagnostics. Refuse to compile the program if you `i /= 0`. Refuse to compile if you `arr[arr.len]`. This is the level of stupid optimizer sees, once it normalizes your code, but proceeds to protect your feelings. My goal so for hblang to relay this to you as much as possible. If we can query for optimizations, we can query for bugs too.

View file

@ -1,8 +0,0 @@
### About posting code
If you are unfammiliar with [hblang](https://git.ablecorp.us/AbleOS/holey-bytes), refer to the **hblang/README.md** or vizit [mlokis'es posts](/profile/mlokis). Preferably don't edit the code here.
### Extra textarea features
- proper tab behaviour
- snap to previous tab boundary on "empty" lines

View file

@ -1,11 +0,0 @@
## Welcome to depell
Depell (dependency hell) is a simple "social" media site, except that all you can post is [hblang](https://git.ablecorp.us/AbleOS/holey-bytes) code. Instead of likes you run the program, and instead of mentions you import the program as dependency. Run counts even when ran indirectly.
The backend only serves the code and frontend compiles and runs it locally. All posts are immutable.
## Security?
All code runs in WASM (inside a holey-bytes VM until hblang compiles to wasm) and is controlled by JavaScript. WASM
cant do any form of IO without going trough JavaScript so as long as JS import does not allow wasm to execute
arbitrary JS code, WASM can act as a container inside the JS.

View file

@ -0,0 +1,17 @@
<h1>Welcome to depell</h1>
<p>
Depell (dependency hell) is a simple "social" media site best compared to twitter, except that all you can post is
<a href="https://git.ablecorp.us/AbleOS/holey-bytes">hblang</a> code with no comments allowed. Instead of likes you
run the program, and instead of retweets you import the program as dependency. Run counts even when ran indirectly.
</p>
<p>
The backend only serves the code and frontend compiles and runs it locally. All posts are immutable.
</p>
<h2>Security?</h2>
<p>
All code runs in WASM (inside a holey-bytes VM until hblang compiles to wasm) and is controlled by JavaScript. WASM
cant do any form of IO without going trough JavaScript so as long as JS import does not allow wasm to execute
arbitrary JS code, WASM can act as a container inside the JS.
</p>

View file

@ -27,16 +27,8 @@ unsafe extern "C" fn fmt() {
OUTPUT_LEN = MAX_OUTPUT_SIZE - f.0.len();
}
#[no_mangle]
unsafe extern "C" fn tok() {
let code = core::slice::from_raw_parts_mut(
core::ptr::addr_of_mut!(OUTPUT).cast(), OUTPUT_LEN);
OUTPUT_LEN = fmt::get_token_kinds(code);
}
#[no_mangle]
unsafe extern "C" fn minify() {
let code = core::str::from_raw_parts_mut(
core::ptr::addr_of_mut!(OUTPUT).cast(), OUTPUT_LEN);
let code = core::str::from_raw_parts_mut(core::ptr::addr_of_mut!(OUTPUT).cast(), OUTPUT_LEN);
OUTPUT_LEN = fmt::minify(code);
}

View file

@ -4,12 +4,9 @@
use {
alloc::{string::String, vec::Vec},
core::ffi::CStr,
hblang::{
backend::hbvm::HbvmBackend,
son::{Codegen, CodegenCtx},
ty::Module,
Ent,
parser::FileId,
son::{hbvm::HbvmBackend, Codegen, CodegenCtx},
},
};
@ -63,7 +60,7 @@ unsafe fn compile_and_run(mut fuel: usize) {
let files = {
let paths = files.iter().map(|f| f.path).collect::<Vec<_>>();
let mut loader = |path: &str, _: &str, kind| match kind {
hblang::parser::FileKind::Module => Ok(paths.binary_search(&path).unwrap()),
hblang::parser::FileKind::Module => Ok(paths.binary_search(&path).unwrap() as FileId),
hblang::parser::FileKind::Embed => Err("embeds are not supported".into()),
};
files
@ -82,7 +79,7 @@ unsafe fn compile_and_run(mut fuel: usize) {
let mut ct = {
let mut backend = HbvmBackend::default();
Codegen::new(&mut backend, &files, &mut ctx).generate(Module::new(root));
Codegen::new(&mut backend, &files, &mut ctx).generate(root as FileId);
if !ctx.parser.errors.borrow().is_empty() {
log::error!("{}", ctx.parser.errors.borrow());
@ -100,15 +97,8 @@ unsafe fn compile_and_run(mut fuel: usize) {
break;
}
Ok(hbvm::VmRunOk::Ecall) => {
let kind = ct.vm.read_reg(2).0;
match kind {
0 => {
let str = ct.vm.read_reg(3).0;
let str = unsafe { CStr::from_ptr(str as _) };
log::error!("{}", str.to_str().unwrap());
}
unknown => log::error!("unknown ecall: {unknown}"),
}
let unknown = ct.vm.read_reg(2).0;
log::error!("unknown ecall: {unknown}")
}
Ok(hbvm::VmRunOk::Timer) => {
fuel -= 1;

View file

@ -12,12 +12,17 @@ name = "fuzz"
path = "src/fuzz_main.rs"
[dependencies]
hashbrown = { version = "0.15.0", default-features = false, features = ["raw-entry", "allocator-api2"] }
hbbytecode = { workspace = true, features = ["disasm"] }
hbvm = { workspace = true, features = ["nightly", "alloc"] }
hashbrown = { version = "0.15.0", default-features = false, features = ["raw-entry"] }
hbvm = { workspace = true, features = ["nightly"] }
log = "0.4.22"
[dependencies.regalloc2]
git = "https://github.com/jakubDoka/regalloc2"
branch = "reuse-allocations"
default-features = false
[features]
default = ["std"]
default = ["std", "regalloc2/trace-log"]
std = []
no_log = ["log/max_level_off"]

File diff suppressed because one or more lines are too long

View file

@ -1,35 +0,0 @@
use std::{fmt::Write, iter};
fn main() {
const TEST_FILE: &str = "src/testcases.rs";
const INPUT: &str = include_str!("./README.md");
let mut out = String::new();
for (name, code) in block_iter(INPUT) {
let name = name.replace(' ', "_");
_ = writeln!(
out,
"#[test] fn {name}() {{ run_codegen_test(\"{name}\", r##\"{code}\"##) }}"
);
}
std::fs::write(TEST_FILE, out).unwrap();
}
fn block_iter(mut input: &str) -> impl Iterator<Item = (&str, &str)> {
const CASE_PREFIX: &str = "#### ";
const CASE_SUFFIX: &str = "\n```hb";
iter::from_fn(move || loop {
let pos = input.find(CASE_PREFIX)?;
input = unsafe { input.get_unchecked(pos + CASE_PREFIX.len()..) };
let Some((test_name, rest)) = input.split_once(CASE_SUFFIX) else { continue };
if !test_name.chars().all(|c| c.is_alphanumeric() || c == '_') {
continue;
}
input = rest;
let (body, rest) = input.split_once("```").unwrap_or((input, ""));
input = rest;
break Some((test_name, body));
})
}

View file

@ -1,5 +1,4 @@
--fmt - format all imported source files
--fmt-stdout - dont write the formatted file but print it
--dump-asm - output assembly instead of raw code, (the assembly is more for debugging the compiler)
--threads <1...> - number of extra threads compiler can use [default: 0]
--path-resolver <name> - choose between builtin path resolvers, options are: ableos
--fmt - format all imported source files
--fmt-stdout - dont write the formatted file but print it
--dump-asm - output assembly instead of raw code, (the assembly is more for debugging the compiler)
--threads <1...> - number of extra threads compiler can use [default: 0]

File diff suppressed because it is too large Load diff

View file

@ -1,15 +1,9 @@
use {
crate::{
lexer::{self, Lexer, TokenKind},
parser::{
self, CommentOr, CtorField, EnumField, Expr, FieldList, ListKind, Poser, Radix,
StructField, UnionField,
},
},
core::{
fmt::{self},
mem,
parser::{self, CommentOr, CtorField, Expr, Poser, Radix, StructField},
},
core::fmt::{self},
};
pub fn display_radix(radix: Radix, mut value: u64, buf: &mut [u8; 64]) -> &str {
@ -32,71 +26,6 @@ pub fn display_radix(radix: Radix, mut value: u64, buf: &mut [u8; 64]) -> &str {
unreachable!()
}
#[repr(u8)]
enum TokenGroup {
Blank,
Comment,
Keyword,
Identifier,
Directive,
Number,
String,
Op,
Assign,
Paren,
Bracket,
Colon,
Comma,
Dot,
Ctor,
}
impl TokenKind {
fn to_higlight_group(self) -> TokenGroup {
use {TokenGroup as TG, TokenKind::*};
match self {
BSlash | Pound | Eof | Ct => TG::Blank,
Comment => TG::Comment,
Directive => TG::Directive,
Colon => TG::Colon,
Semi | Comma => TG::Comma,
Dot => TG::Dot,
Ctor | Arr | Tupl | TArrow | Range => TG::Ctor,
LParen | RParen => TG::Paren,
LBrace | RBrace | LBrack | RBrack => TG::Bracket,
Number | Float => TG::Number,
Under | CtIdent | Ident => TG::Identifier,
Tick | Tilde | Que | Not | Mod | Band | Bor | Xor | Mul | Add | Sub | Div | Shl
| Shr | Or | And | Lt | Gt | Eq | Le | Ge | Ne => TG::Op,
Decl | Assign | BorAss | XorAss | BandAss | AddAss | SubAss | MulAss | DivAss
| ModAss | ShrAss | ShlAss => TG::Assign,
DQuote | Quote => TG::String,
Slf | Defer | Return | If | Else | Loop | Break | Continue | Fn | Idk | Die
| Struct | Packed | True | False | Null | Match | Enum | Union | CtLoop => TG::Keyword,
}
}
}
pub fn get_token_kinds(mut source: &mut [u8]) -> usize {
let len = source.len();
loop {
let src = unsafe { core::str::from_utf8_unchecked(source) };
let mut token = lexer::Lexer::new(src).eat();
match token.kind {
TokenKind::Eof => break,
// ???
TokenKind::CtIdent | TokenKind::Directive => token.start -= 1,
_ => {}
}
let start = token.start as usize;
let end = token.end as usize;
source[..start].fill(0);
source[start..end].fill(token.kind.to_higlight_group() as u8);
source = &mut source[end..];
}
len
}
pub fn minify(source: &mut str) -> usize {
fn needs_space(c: u8) -> bool {
matches!(c, b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | 127..)
@ -110,7 +39,7 @@ pub fn minify(source: &mut str) -> usize {
let mut token = lexer::Lexer::new(reader).eat();
match token.kind {
TokenKind::Eof => break,
TokenKind::CtIdent | TokenKind::CtLoop | TokenKind::Directive => token.start -= 1,
TokenKind::CtIdent | TokenKind::Directive => token.start -= 1,
_ => {}
}
@ -206,30 +135,24 @@ impl<'a> Formatter<'a> {
return f.write_str(end);
}
if !end.is_empty() {
writeln!(f)?;
}
self.depth += !end.is_empty() as usize;
let mut already_indented = end.is_empty();
writeln!(f)?;
self.depth += 1;
let res = (|| {
for (i, stmt) in list.iter().enumerate() {
if !mem::take(&mut already_indented) {
for _ in 0..self.depth {
f.write_str("\t")?;
}
for _ in 0..self.depth {
f.write_str("\t")?;
}
let add_sep = fmt(self, stmt, f)?;
if add_sep {
f.write_str(sep)?;
}
if let Some(expr) = list.get(i + 1)
&& let Some(prev) = self.source.get(..expr.posi() as usize)
&& let Some(rest) = self.source.get(expr.posi() as usize..)
{
if sep.is_empty() && prev.trim_end().ends_with(';') {
if insert_needed_semicolon(rest) {
f.write_str(";")?;
}
if count_trailing_newlines(prev) > 1 {
if preserve_newlines(&self.source[..expr.posi() as usize]) > 1 {
f.write_str("\n")?;
}
}
@ -239,14 +162,12 @@ impl<'a> Formatter<'a> {
}
Ok(())
})();
self.depth -= !end.is_empty() as usize;
self.depth -= 1;
if !end.is_empty() {
for _ in 0..self.depth {
f.write_str("\t")?;
}
f.write_str(end)?;
for _ in 0..self.depth {
f.write_str("\t")?;
}
f.write_str(end)?;
res
}
@ -265,32 +186,6 @@ impl<'a> Formatter<'a> {
}
}
fn fmt_fields<F: core::fmt::Write, T: Poser + Copy>(
&mut self,
f: &mut F,
keyword: &str,
trailing_comma: bool,
fields: FieldList<T>,
fmt: impl Fn(&mut Self, &T, &mut F) -> Result<(), fmt::Error>,
) -> fmt::Result {
f.write_str(keyword)?;
f.write_str(" {")?;
self.fmt_list_low(f, trailing_comma, "}", ",", fields, |s, field, f| {
match field {
CommentOr::Or(Ok(field)) => fmt(s, field, f)?,
CommentOr::Or(Err(scope)) => {
s.fmt_list(f, true, "", "", scope, Self::fmt)?;
return Ok(false);
}
CommentOr::Comment { literal, .. } => {
f.write_str(literal)?;
f.write_str("\n")?;
}
}
Ok(field.or().is_some())
})
}
pub fn fmt<F: core::fmt::Write>(&mut self, expr: &Expr, f: &mut F) -> fmt::Result {
macro_rules! impl_parenter {
($($name:ident => $pat:pat,)*) => {
@ -307,13 +202,11 @@ impl<'a> Formatter<'a> {
}
match *expr {
Expr::Defer { value, .. } => {
f.write_str("defer ")?;
Expr::Ct { value, .. } => {
f.write_str("$: ")?;
self.fmt(value, f)
}
Expr::Slf { .. } => f.write_str("Self"),
Expr::String { literal, .. } => f.write_str(literal),
Expr::Char { literal, .. } => f.write_str(literal),
Expr::Comment { literal, .. } => f.write_str(literal),
Expr::Mod { path, .. } => write!(f, "@use(\"{path}\")"),
Expr::Embed { path, .. } => write!(f, "@embed(\"{path}\")"),
@ -322,16 +215,6 @@ impl<'a> Formatter<'a> {
f.write_str(".")?;
f.write_str(field)
}
Expr::Range { start, end, .. } => {
if let Some(start) = start {
self.fmt(start, f)?;
}
f.write_str("..")?;
if let Some(end) = end {
self.fmt(end, f)?;
}
Ok(())
}
Expr::Directive { name, args, .. } => {
f.write_str("@")?;
f.write_str(name)?;
@ -343,44 +226,25 @@ impl<'a> Formatter<'a> {
f.write_str("packed ")?;
}
self.fmt_fields(
f,
"struct",
trailing_comma,
fields,
|s, StructField { name, ty, default_value, .. }, f| {
f.write_str(name)?;
f.write_str(": ")?;
s.fmt(ty, f)?;
if let Some(deva) = default_value {
f.write_str(" = ")?;
s.fmt(deva, f)?;
write!(f, "struct {{")?;
self.fmt_list_low(f, trailing_comma, "}", ",", fields, |s, field, f| {
match field {
CommentOr::Or(StructField { name, ty, .. }) => {
f.write_str(name)?;
f.write_str(": ")?;
s.fmt(ty, f)?
}
Ok(())
},
)
CommentOr::Comment { literal, .. } => {
f.write_str(literal)?;
f.write_str("\n")?;
}
}
Ok(field.or().is_some())
})
}
Expr::Union { fields, trailing_comma, .. } => self.fmt_fields(
f,
"union",
trailing_comma,
fields,
|s, UnionField { name, ty, .. }, f| {
f.write_str(name)?;
f.write_str(": ")?;
s.fmt(ty, f)
},
),
Expr::Enum { variants, trailing_comma, .. } => self.fmt_fields(
f,
"enum",
trailing_comma,
variants,
|_, EnumField { name, .. }, f| f.write_str(name),
),
Expr::Ctor { ty, fields, trailing_comma, .. } => {
if let Some(ty) = ty {
self.fmt_paren(ty, f, postfix)?;
self.fmt_paren(ty, f, unary)?;
}
f.write_str(".{")?;
self.fmt_list(
@ -399,43 +263,38 @@ impl<'a> Formatter<'a> {
},
)
}
Expr::List {
Expr::Tupl {
pos,
kind: term,
ty: Some(&Expr::Slice { pos: spos, size: Some(&Expr::Number { value, .. }), item }),
fields,
trailing_comma,
} if value as usize == fields.len() => self.fmt(
&Expr::List {
&Expr::Tupl {
pos,
kind: term,
ty: Some(&Expr::Slice { pos: spos, size: None, item }),
fields,
trailing_comma,
},
f,
),
Expr::List { ty, kind: term, fields, trailing_comma, .. } => {
Expr::Tupl { ty, fields, trailing_comma, .. } => {
if let Some(ty) = ty {
self.fmt_paren(ty, f, postfix)?;
self.fmt_paren(ty, f, unary)?;
}
let (start, end) = match term {
ListKind::Tuple => (".(", ")"),
ListKind::Array => (".[", "]"),
};
f.write_str(start)?;
self.fmt_list(f, trailing_comma, end, ",", fields, Self::fmt)
f.write_str(".(")?;
self.fmt_list(f, trailing_comma, ")", ",", fields, Self::fmt)
}
Expr::Slice { item, size, .. } => {
f.write_str("[")?;
self.fmt(item, f)?;
if let Some(size) = size {
f.write_str("; ")?;
self.fmt(size, f)?;
}
f.write_str("]")?;
self.fmt_paren(item, f, unary)
f.write_str("]")
}
Expr::Index { base, index } => {
self.fmt_paren(base, f, postfix)?;
self.fmt(base, f)?;
f.write_str("[")?;
self.fmt(index, f)?;
f.write_str("]")
@ -457,18 +316,8 @@ impl<'a> Formatter<'a> {
}
Ok(())
}
Expr::Match { value, branches, .. } => {
f.write_str("match ")?;
self.fmt(value, f)?;
f.write_str(" {")?;
self.fmt_list(f, true, "}", ",", branches, |s, br, f| {
s.fmt(&br.pat, f)?;
f.write_str(" => ")?;
s.fmt(&br.body, f)
})
}
Expr::Loop { body, unrolled, .. } => {
f.write_str(if unrolled { "$loop " } else { "loop " })?;
Expr::Loop { body, .. } => {
f.write_str("loop ")?;
self.fmt(body, f)
}
Expr::Closure { ret, body, args, .. } => {
@ -558,7 +407,7 @@ impl<'a> Formatter<'a> {
prev.rfind(|c: char| c.is_ascii_whitespace()).map_or(prev.len(), |i| i + 1);
let exact_bound = lexer::Lexer::new(&prev[estimate_bound..]).last().start;
prev = &prev[..exact_bound as usize + estimate_bound];
if count_trailing_newlines(prev) > 0 {
if preserve_newlines(prev) > 0 {
f.write_str("\n")?;
for _ in 0..self.depth + 1 {
f.write_str("\t")?;
@ -566,9 +415,7 @@ impl<'a> Formatter<'a> {
f.write_str(op.name())?;
f.write_str(" ")?;
} else {
if op != TokenKind::Colon {
f.write_str(" ")?;
}
f.write_str(" ")?;
f.write_str(op.name())?;
f.write_str(" ")?;
}
@ -583,10 +430,15 @@ impl<'a> Formatter<'a> {
}
}
pub fn count_trailing_newlines(source: &str) -> usize {
pub fn preserve_newlines(source: &str) -> usize {
source[source.trim_end().len()..].bytes().filter(|&c| c == b'\n').count()
}
pub fn insert_needed_semicolon(source: &str) -> bool {
let kind = lexer::Lexer::new(source).eat().kind;
kind.precedence().is_some() || matches!(kind, TokenKind::Ctor | TokenKind::Tupl)
}
impl core::fmt::Display for parser::Ast {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt_file(self.exprs(), &self.file, f)
@ -597,14 +449,14 @@ pub fn fmt_file(exprs: &[Expr], file: &str, f: &mut impl fmt::Write) -> fmt::Res
for (i, expr) in exprs.iter().enumerate() {
Formatter::new(file).fmt(expr, f)?;
if let Some(expr) = exprs.get(i + 1)
&& let Some(prefix) = file.get(..expr.pos() as usize)
&& let Some(rest) = file.get(expr.pos() as usize..)
{
if prefix.trim_end().ends_with(';') {
f.write_str(";")?;
if insert_needed_semicolon(rest) {
write!(f, ";")?;
}
if count_trailing_newlines(prefix) > 1 {
f.write_str("\n")?;
if preserve_newlines(&file[..expr.pos() as usize]) > 1 {
writeln!(f)?;
}
}
@ -630,7 +482,15 @@ pub mod test {
let mut ctx = Ctx::default();
let ast = parser::Ast::new(ident, minned, &mut ctx, &mut parser::no_loader);
log::info!("{}", ctx.errors.borrow());
//log::error!(
// "{} / {} = {} | {} / {} = {}",
// ast.mem.size(),
// input.len(),
// ast.mem.size() as f32 / input.len() as f32,
// ast.mem.size(),
// ast.file.len(),
// ast.mem.size() as f32 / ast.file.len() as f32
//);
let mut output = String::new();
write!(output, "{ast}").unwrap();

View file

@ -1,15 +1,12 @@
use {
crate::{
backend::hbvm::HbvmBackend,
parser::{Ast, Ctx, FileKind},
son::{self},
ty, FnvBuildHasher,
parser::{self, Ast, Ctx, FileKind},
son::{self, hbvm::HbvmBackend},
},
alloc::{string::String, vec::Vec},
core::{fmt::Write, num::NonZeroUsize, ops::Deref},
hashbrown::hash_map,
std::{
borrow::ToOwned,
collections::VecDeque,
eprintln,
ffi::OsStr,
@ -20,8 +17,6 @@ use {
},
};
type HashMap<K, V> = hashbrown::HashMap<K, V, FnvBuildHasher>;
pub struct Logger;
impl log::Log for Logger {
@ -38,51 +33,19 @@ impl log::Log for Logger {
fn flush(&self) {}
}
pub const ABLEOS_PATH_RESOLVER: PathResolver =
&|mut path: &str, mut from: &str, tmp: &mut PathBuf| {
tmp.clear();
path = match path {
"stn" => {
from = "";
"./sysdata/libraries/stn/src/lib.hb"
}
_ => path,
};
match path.split_once(':') {
Some(("lib", p)) => tmp.extend(["./sysdata/libraries", p, "src/lib.hb"]),
Some(("stn", p)) => {
tmp.extend(["./sysdata/libraries/stn/src", &(p.to_owned() + ".hb")])
}
Some(("sysdata", p)) => tmp.extend(["./sysdata", p]),
None => match Path::new(from).parent() {
Some(parent) => tmp.extend([parent, Path::new(path)]),
None => tmp.push(path),
},
_ => panic!("path: '{path}' is invalid: unexpected ':'"),
};
tmp.canonicalize().map_err(|source| CantLoadFile { path: std::mem::take(tmp), source })
};
#[derive(Default)]
pub struct Options<'a> {
pub struct Options {
pub fmt: bool,
pub fmt_stdout: bool,
pub dump_asm: bool,
pub extra_threads: usize,
pub resolver: Option<PathResolver<'a>>,
}
impl<'a> Options<'a> {
pub fn from_args(
args: &[&str],
out: &mut Vec<u8>,
resolvers: &'a [(&str, PathResolver)],
) -> std::io::Result<Self> {
impl Options {
pub fn from_args(args: &[&str]) -> std::io::Result<Self> {
if args.contains(&"--help") || args.contains(&"-h") {
writeln!(out, "Usage: hbc [OPTIONS...] <FILE>")?;
writeln!(out, include_str!("../command-help.txt"))?;
log::error!("Usage: hbc [OPTIONS...] <FILE>");
log::error!(include_str!("../command-help.txt"));
return Err(std::io::ErrorKind::Other.into());
}
@ -95,91 +58,65 @@ impl<'a> Options<'a> {
.position(|&a| a == "--threads")
.map(|i| {
args[i + 1].parse::<NonZeroUsize>().map_err(|e| {
writeln!(out, "--threads expects non zero integer: {e}")
.err()
.unwrap_or(std::io::ErrorKind::Other.into())
std::io::Error::other(format!("--threads expects non zero integer: {e}"))
})
})
.transpose()?
.map_or(1, NonZeroUsize::get)
- 1,
resolver: args
.iter()
.position(|&a| a == "--path-resolver")
.map(|i| {
resolvers.iter().find(|&&(n, _)| args[i + 1] == n).map(|&(_, r)| r).ok_or_else(
|| {
writeln!(
out,
"--path-resolver can only be one of: {}",
resolvers
.iter()
.map(|&(n, _)| n)
.intersperse(", ")
.collect::<String>()
)
.err()
.unwrap_or(std::io::ErrorKind::Other.into())
},
)
})
.transpose()?,
})
}
}
pub fn run_compiler(
root_file: &str,
options: Options,
out: &mut Vec<u8>,
warnings: &mut String,
) -> std::io::Result<()> {
let parsed = parse_from_fs(
options.extra_threads,
root_file,
options.resolver.unwrap_or(&default_resolve),
)?;
pub fn run_compiler(root_file: &str, options: Options, out: &mut Vec<u8>) -> std::io::Result<()> {
let parsed = parse_from_fs(options.extra_threads, root_file)?;
if (options.fmt || options.fmt_stdout) && !parsed.errors.is_empty() {
*out = parsed.errors.into_bytes();
return Err(std::io::Error::other("fmt fialed (errors are in out)"));
fn format_ast(ast: parser::Ast) -> std::io::Result<()> {
let mut output = String::new();
write!(output, "{ast}").unwrap();
if ast.file.deref().trim() != output.as_str().trim() {
std::fs::write(&*ast.path, output)?;
}
Ok(())
}
if options.fmt {
let mut output = String::new();
for ast in parsed.ast {
write!(output, "{ast}").unwrap();
if ast.file.deref().trim() != output.as_str().trim() {
std::fs::write(&*ast.path, &output)?;
}
output.clear();
if !parsed.errors.is_empty() {
*out = parsed.errors.into_bytes();
return Err(std::io::Error::other("parsing fialed"));
}
for parsed in parsed.ast {
format_ast(parsed)?;
}
} else if options.fmt_stdout {
write!(out, "{}", &parsed.ast[0])?;
if !parsed.errors.is_empty() {
*out = parsed.errors.into_bytes();
return Err(std::io::Error::other("parsing fialed"));
}
let ast = parsed.ast.into_iter().next().unwrap();
write!(out, "{ast}").unwrap();
} else {
let mut backend = HbvmBackend::default();
let mut ctx = crate::son::CodegenCtx::default();
*ctx.parser.errors.get_mut() = parsed.errors;
let mut codegen = son::Codegen::new(&mut backend, &parsed.ast, &mut ctx);
codegen.push_embeds(parsed.embeds);
codegen.generate(ty::Module::MAIN);
*warnings = core::mem::take(&mut *codegen.warnings.borrow_mut());
codegen.push_embeds(parsed.embeds);
codegen.generate(0);
if !codegen.errors.borrow().is_empty() {
drop(codegen);
*out = ctx.parser.errors.into_inner().into_bytes();
return Err(std::io::Error::other("compilation faoled (errors are in out)"));
log::error!("{}", codegen.errors.borrow());
return Err(std::io::Error::other("compilation faoled"));
}
codegen.assemble(out);
if options.dump_asm {
let mut disasm = String::new();
let err = codegen.disasm(&mut disasm, out).map_err(|e| io::Error::other(e.to_string()));
codegen.disasm(&mut disasm, out).map_err(|e| io::Error::other(e.to_string()))?;
*out = disasm.into_bytes();
err?
}
}
@ -277,53 +214,45 @@ pub struct Loaded {
errors: String,
}
fn default_resolve(path: &str, from: &str, tmp: &mut PathBuf) -> Result<PathBuf, CantLoadFile> {
tmp.clear();
match Path::new(from).parent() {
Some(parent) => tmp.extend([parent, Path::new(path)]),
None => tmp.push(path),
};
pub fn parse_from_fs(extra_threads: usize, root: &str) -> io::Result<Loaded> {
fn resolve(path: &str, from: &str, tmp: &mut PathBuf) -> Result<PathBuf, CantLoadFile> {
tmp.clear();
match Path::new(from).parent() {
Some(parent) => tmp.extend([parent, Path::new(path)]),
None => tmp.push(path),
};
tmp.canonicalize().map_err(|source| CantLoadFile { path: std::mem::take(tmp), source })
}
/// fn(path, from, tmp)
pub type PathResolver<'a> =
&'a (dyn Fn(&str, &str, &mut PathBuf) -> Result<PathBuf, CantLoadFile> + Send + Sync);
#[derive(Debug)]
pub struct CantLoadFile {
pub path: PathBuf,
pub source: io::Error,
}
impl core::fmt::Display for CantLoadFile {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "can't load file: {}", display_rel_path(&self.path),)
tmp.canonicalize().map_err(|source| CantLoadFile { path: std::mem::take(tmp), source })
}
}
impl core::error::Error for CantLoadFile {
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
Some(&self.source)
#[derive(Debug)]
struct CantLoadFile {
path: PathBuf,
source: io::Error,
}
}
impl From<CantLoadFile> for io::Error {
fn from(e: CantLoadFile) -> Self {
io::Error::new(io::ErrorKind::InvalidData, e)
impl core::fmt::Display for CantLoadFile {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "can't load file: {}", display_rel_path(&self.path),)
}
}
}
pub fn parse_from_fs(
extra_threads: usize,
root: &str,
resolve: PathResolver,
) -> io::Result<Loaded> {
type Task = (usize, PathBuf);
impl core::error::Error for CantLoadFile {
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
Some(&self.source)
}
}
let seen_modules = Mutex::new(HashMap::<PathBuf, usize>::default());
let seen_embeds = Mutex::new(HashMap::<PathBuf, usize>::default());
impl From<CantLoadFile> for io::Error {
fn from(e: CantLoadFile) -> Self {
io::Error::new(io::ErrorKind::InvalidData, e)
}
}
type Task = (u32, PathBuf);
let seen_modules = Mutex::new(crate::HashMap::<PathBuf, u32>::default());
let seen_embeds = Mutex::new(crate::HashMap::<PathBuf, u32>::default());
let tasks = TaskQueue::<Task>::new(extra_threads + 1);
let ast = Mutex::new(Vec::<io::Result<Ast>>::new());
let embeds = Mutex::new(Vec::<Vec<u8>>::new());
@ -342,7 +271,7 @@ pub fn parse_from_fs(
}
hash_map::Entry::Vacant(entry) => {
physiscal_path = entry.insert_entry(len as _).key().clone();
len
len as u32
}
}
};
@ -367,7 +296,7 @@ pub fn parse_from_fs(
}
hash_map::Entry::Vacant(entry) => {
physiscal_path = entry.insert_entry(len as _).key().clone();
len
len as u32
}
}
};
@ -382,10 +311,10 @@ pub fn parse_from_fs(
)
})?;
let mut embeds = embeds.lock().unwrap();
if id >= embeds.len() {
embeds.resize(id + 1, Default::default());
if id as usize >= embeds.len() {
embeds.resize(id as usize + 1, Default::default());
}
embeds[id] = content;
embeds[id as usize] = content;
Ok(id)
}
}
@ -409,9 +338,9 @@ pub fn parse_from_fs(
while let Some(task @ (indx, ..)) = tasks.pop() {
let res = execute_task(&mut ctx, task, &mut tmp);
let mut ast = ast.lock().unwrap();
let len = ast.len().max(indx + 1);
let len = ast.len().max(indx as usize + 1);
ast.resize_with(len, || Err(io::ErrorKind::InvalidData.into()));
ast[indx] = res;
ast[indx as usize] = res;
}
ctx.errors.into_inner()
};

View file

@ -1,10 +1,8 @@
use {
crate::{
backend::hbvm::HbvmBackend,
lexer::TokenKind,
parser,
son::{Codegen, CodegenCtx},
ty::Module,
son::{hbvm::HbvmBackend, Codegen, CodegenCtx},
},
alloc::string::String,
core::{fmt::Write, hash::BuildHasher, ops::Range},
@ -137,6 +135,6 @@ pub fn fuzz(seed_range: Range<u64>) {
let mut backend = HbvmBackend::default();
let mut cdg = Codegen::new(&mut backend, core::slice::from_ref(&parsed), &mut ctx);
cdg.generate(Module::MAIN);
cdg.generate(0);
}
}

View file

@ -32,9 +32,6 @@ macro_rules! gen_token_kind {
#[keywords] $(
$keyword:ident = $keyword_lit:literal,
)*
#[const_keywords] $(
$const_keyword:ident = $const_keyword_lit:literal,
)*
#[punkt] $(
$punkt:ident = $punkt_lit:literal,
)*
@ -59,7 +56,6 @@ macro_rules! gen_token_kind {
match *self {
$( Self::$pattern => concat!('<', stringify!($pattern), '>'), )*
$( Self::$keyword => stringify!($keyword_lit), )*
$( Self::$const_keyword => concat!('$', $const_keyword_lit), )*
$( Self::$punkt => stringify!($punkt_lit), )*
$($( Self::$op => $op_lit,
$(Self::$assign => concat!($op_lit, "="),)?)*)*
@ -76,23 +72,12 @@ macro_rules! gen_token_kind {
} + 1)
}
#[allow(non_upper_case_globals)]
fn from_ident(ident: &[u8]) -> Self {
$(const $keyword: &[u8] = $keyword_lit.as_bytes();)*
match ident {
$($keyword => Self::$keyword,)*
$($keyword_lit => Self::$keyword,)*
_ => Self::Ident,
}
}
#[allow(non_upper_case_globals)]
fn from_ct_ident(ident: &[u8]) -> Self {
$(const $const_keyword: &[u8] = $const_keyword_lit.as_bytes();)*
match ident {
$($const_keyword => Self::$const_keyword,)*
_ => Self::CtIdent,
}
}
}
};
}
@ -136,11 +121,23 @@ pub enum TokenKind {
Ct,
Return,
If,
Else,
Loop,
Break,
Continue,
Fn,
Struct,
Packed,
True,
False,
Null,
Idk,
Die,
Ctor,
Tupl,
Arr,
TArrow,
Range,
Or,
And,
@ -150,31 +147,8 @@ pub enum TokenKind {
BSlash = b'\\',
RBrack = b']',
Xor = b'^',
Under = b'_',
Tick = b'`',
Slf,
Return,
If,
Match,
Else,
Loop,
Break,
Continue,
Fn,
Struct,
Packed,
Enum,
Union,
True,
False,
Null,
Idk,
Die,
Defer,
CtLoop,
Under = b'_',
// Unused = a-z
LBrace = b'{',
Bor = b'|',
@ -219,10 +193,6 @@ impl TokenKind {
matches!(self, S::Eq | S::Ne | S::Bor | S::Xor | S::Band | S::Add | S::Mul)
}
pub fn is_compatison(self) -> bool {
matches!(self, Self::Lt | Self::Gt | Self::Ge | Self::Le | Self::Ne | Self::Eq)
}
pub fn is_supported_float_op(self) -> bool {
matches!(
self,
@ -293,11 +263,12 @@ impl TokenKind {
match self {
Self::Sub if float => (-f64::from_bits(value as _)).to_bits() as _,
Self::Sub => value.wrapping_neg(),
Self::Not => (value == 0) as _,
Self::Float if float => value,
Self::Float => (value as f64).to_bits() as _,
Self::Number if float => f64::from_bits(value as _) as _,
Self::Number => value,
Self::Number => {
debug_assert!(float);
f64::from_bits(value as _).to_bits() as _
}
s => todo!("{s}"),
}
}
@ -324,34 +295,24 @@ gen_token_kind! {
Eof,
Directive,
#[keywords]
Slf = "Self",
Return = "return",
If = "if",
Match = "match",
Else = "else",
Loop = "loop",
Break = "break",
Continue = "continue",
Fn = "fn",
Struct = "struct",
Packed = "packed",
Enum = "enum",
Union = "union",
True = "true",
False = "false",
Null = "null",
Idk = "idk",
Die = "die",
Defer = "defer",
Under = "_",
#[const_keywords]
CtLoop = "loop",
Return = b"return",
If = b"if",
Else = b"else",
Loop = b"loop",
Break = b"break",
Continue = b"continue",
Fn = b"fn",
Struct = b"struct",
Packed = b"packed",
True = b"true",
False = b"false",
Null = b"null",
Idk = b"idk",
Die = b"die",
Under = b"_",
#[punkt]
Ctor = ".{",
Tupl = ".(",
Arr = ".[",
TArrow = "=>",
Range = "..",
// #define OP: each `#[prec]` delimeters a level of precedence from lowest to highest
#[ops]
#[prec]
@ -430,23 +391,6 @@ impl<'a> Lexer<'a> {
unsafe { core::str::from_utf8_unchecked(&self.source[tok]) }
}
pub fn taste(&self) -> Token {
Lexer { pos: self.pos, source: self.source }.eat()
}
fn peek_n<const N: usize>(&self) -> Option<&[u8; N]> {
if core::intrinsics::unlikely(self.pos as usize + N > self.source.len()) {
None
} else {
Some(unsafe {
self.source
.get_unchecked(self.pos as usize..self.pos as usize + N)
.first_chunk()
.unwrap_unchecked()
})
}
}
fn peek(&self) -> Option<u8> {
if core::intrinsics::unlikely(self.pos >= self.source.len() as u32) {
None
@ -515,11 +459,7 @@ impl<'a> Lexer<'a> {
self.advance();
}
if self
.peek_n()
.map_or_else(|| self.peek() == Some(b'.'), |&[a, b]| a == b'.' && b != b'.')
{
self.pos += 1;
if self.advance_if(b'.') {
while let Some(b'0'..=b'9') = self.peek() {
self.advance();
}
@ -571,23 +511,14 @@ impl<'a> Lexer<'a> {
}
b'.' if self.advance_if(b'{') => T::Ctor,
b'.' if self.advance_if(b'(') => T::Tupl,
b'.' if self.advance_if(b'[') => T::Arr,
b'.' if self.advance_if(b'.') => T::Range,
b'=' if self.advance_if(b'>') => T::TArrow,
b'&' if self.advance_if(b'&') => T::And,
b'|' if self.advance_if(b'|') => T::Or,
b'$' if self.advance_if(b':') => T::Ct,
b'@' => {
b'@' | b'$' => {
start += 1;
advance_ident(self);
identity(c)
}
b'$' => {
start += 1;
advance_ident(self);
let ident = &self.source[start as usize..self.pos as usize];
T::from_ct_ident(ident)
}
b'<' | b'>' if self.advance_if(c) => {
identity(c - 5 + 128 * self.advance_if(b'=') as u8)
}

File diff suppressed because it is too large Load diff

View file

@ -1,31 +1,17 @@
#[cfg(feature = "std")]
fn main() {
fn main() -> std::io::Result<()> {
use std::io::Write;
fn run(out: &mut Vec<u8>, warnings: &mut String) -> std::io::Result<()> {
let args = std::env::args().collect::<Vec<_>>();
let args = args.iter().map(String::as_str).collect::<Vec<_>>();
let resolvers = &[("ableos", hblang::ABLEOS_PATH_RESOLVER)];
let opts = hblang::Options::from_args(&args, out, resolvers)?;
let file = args.iter().filter(|a| !a.starts_with('-')).nth(1).copied().unwrap_or("main.hb");
log::set_logger(&hblang::Logger).unwrap();
log::set_max_level(log::LevelFilter::Info);
hblang::run_compiler(file, opts, out, warnings)
}
let args = std::env::args().collect::<Vec<_>>();
let args = args.iter().map(String::as_str).collect::<Vec<_>>();
log::set_logger(&hblang::fs::Logger).unwrap();
log::set_max_level(log::LevelFilter::Error);
let opts = hblang::Options::from_args(&args)?;
let file = args.iter().filter(|a| !a.starts_with('-')).nth(1).copied().unwrap_or("main.hb");
let mut out = Vec::new();
let mut warnings = String::new();
match run(&mut out, &mut warnings) {
Ok(_) => {
std::io::stderr().write_all(warnings.as_bytes()).unwrap();
std::io::stdout().write_all(&out).unwrap()
}
Err(_) => {
std::io::stderr().write_all(warnings.as_bytes()).unwrap();
std::io::stderr().write_all(&out).unwrap();
std::process::exit(1);
}
}
hblang::run_compiler(file, opts, &mut out)?;
std::io::stdout().write_all(&out)
}

File diff suppressed because it is too large Load diff

View file

@ -2,8 +2,6 @@ use {
crate::{
fmt::Formatter,
lexer::{self, Lexer, Token, TokenKind},
ty::{Global, Module},
utils::Ent as _,
Ident,
},
alloc::{boxed::Box, string::String, vec::Vec},
@ -21,9 +19,10 @@ use {
pub type Pos = u32;
pub type IdentFlags = u32;
pub type FileId = u32;
pub type IdentIndex = u16;
pub type LoaderError = String;
pub type Loader<'a> = &'a mut (dyn FnMut(&str, &str, FileKind) -> Result<usize, LoaderError> + 'a);
pub type Loader<'a> = &'a mut (dyn FnMut(&str, &str, FileKind) -> Result<FileId, LoaderError> + 'a);
#[derive(PartialEq, Eq, Debug)]
pub enum FileKind {
@ -31,7 +30,7 @@ pub enum FileKind {
Embed,
}
pub trait Trans {
trait Trans {
fn trans(self) -> Self;
}
@ -64,7 +63,7 @@ pub mod idfl {
}
}
pub fn no_loader(_: &str, _: &str, _: FileKind) -> Result<usize, LoaderError> {
pub fn no_loader(_: &str, _: &str, _: FileKind) -> Result<FileId, LoaderError> {
Ok(0)
}
@ -79,8 +78,6 @@ struct ScopeIdent {
ident: Ident,
declared: bool,
ordered: bool,
used: bool,
is_ct: bool,
flags: IdentFlags,
}
@ -197,8 +194,8 @@ impl<'a, 'b> Parser<'a, 'b> {
fn declare_rec(&mut self, expr: &Expr, top_level: bool) {
match *expr {
Expr::Ident { pos, id, is_first, is_ct, .. } => {
self.declare(pos, id, !top_level, is_first || top_level, is_ct)
Expr::Ident { pos, id, is_first, .. } => {
self.declare(pos, id, !top_level, is_first || top_level)
}
Expr::Ctor { fields, .. } => {
for CtorField { value, .. } in fields {
@ -209,7 +206,7 @@ impl<'a, 'b> Parser<'a, 'b> {
}
}
fn declare(&mut self, pos: Pos, id: Ident, ordered: bool, valid_order: bool, is_ct: bool) {
fn declare(&mut self, pos: Pos, id: Ident, ordered: bool, valid_order: bool) {
if !valid_order {
self.report(
pos,
@ -231,7 +228,7 @@ impl<'a, 'b> Parser<'a, 'b> {
);
return;
}
self.ctx.idents[index].is_ct = is_ct;
self.ctx.idents[index].ordered = ordered;
}
@ -250,10 +247,7 @@ impl<'a, 'b> Parser<'a, 'b> {
.enumerate()
.rfind(|(_, elem)| self.lexer.slice(elem.ident.range()) == name)
{
Some((i, elem)) => {
elem.used = true;
(i, elem, false)
}
Some((i, elem)) => (i, elem, false),
None => {
let ident = match Ident::new(token.start, name.len() as _) {
None => {
@ -266,9 +260,7 @@ impl<'a, 'b> Parser<'a, 'b> {
self.ctx.idents.push(ScopeIdent {
ident,
declared: false,
used: false,
ordered: false,
is_ct: false,
flags: 0,
});
(self.ctx.idents.len() - 1, self.ctx.idents.last_mut().unwrap(), true)
@ -278,7 +270,7 @@ impl<'a, 'b> Parser<'a, 'b> {
id.flags |= idfl::COMPTIME * is_ct as u32;
if id.declared && id.ordered && self.ns_bound > i {
id.flags |= idfl::COMPTIME;
self.ctx.captured.push(CapturedIdent { id: id.ident, is_ct: id.is_ct });
self.ctx.captured.push(id.ident);
}
(id.ident, bl)
@ -289,27 +281,13 @@ impl<'a, 'b> Parser<'a, 'b> {
}
fn unit_expr(&mut self) -> Option<Expr<'a>> {
self.unit_expr_low(true)
}
fn unit_expr_low(&mut self, eat_tail: bool) -> Option<Expr<'a>> {
use {Expr as E, TokenKind as T};
if matches!(
self.token.kind,
T::RParen | T::RBrace | T::RBrack | T::Comma | T::Semi | T::Else
) {
self.report(self.token.start, "expected expression")?;
}
let frame = self.ctx.idents.len();
let token @ Token { start: pos, .. } = self.next();
let prev_boundary = self.ns_bound;
let prev_captured = self.ctx.captured.len();
let mut must_trail = false;
let mut expr = match token.kind {
T::Defer => E::Defer { pos, value: self.ptr_expr()? },
T::Slf => E::Slf { pos },
T::Ct => E::Ct { pos, value: self.ptr_expr()? },
T::Directive if self.lexer.slice(token.range()) == "use" => {
self.expect_advance(TokenKind::LParen)?;
let str = self.expect_advance(TokenKind::DQuote)?;
@ -321,7 +299,7 @@ impl<'a, 'b> Parser<'a, 'b> {
pos,
path,
id: match (self.loader)(path, self.path, FileKind::Module) {
Ok(id) => Module::new(id),
Ok(id) => id,
Err(e) => {
self.report(str.start, format_args!("error loading dependency: {e:#}"))?
}
@ -339,7 +317,7 @@ impl<'a, 'b> Parser<'a, 'b> {
pos,
path,
id: match (self.loader)(path, self.path, FileKind::Embed) {
Ok(id) => Global::new(id),
Ok(id) => id,
Err(e) => self.report(
str.start,
format_args!("error loading embedded file: {e:#}"),
@ -361,7 +339,6 @@ impl<'a, 'b> Parser<'a, 'b> {
T::Idk => E::Idk { pos },
T::Die => E::Die { pos },
T::DQuote => E::String { pos, literal: self.tok_str(token) },
T::Quote => E::Char { pos, literal: self.tok_str(token) },
T::Packed => {
self.packed = true;
let expr = self.unit_expr()?;
@ -375,62 +352,45 @@ impl<'a, 'b> Parser<'a, 'b> {
expr
}
T::Struct => E::Struct {
pos,
packed: core::mem::take(&mut self.packed),
fields: self.collect_fields(&mut must_trail, |s| {
if s.lexer.taste().kind != T::Colon {
return Some(None);
fields: {
self.ns_bound = self.ctx.idents.len();
self.expect_advance(T::LBrace)?;
self.collect_list(T::Comma, T::RBrace, |s| {
let tok = s.token;
Some(if s.advance_if(T::Comment) {
CommentOr::Comment { literal: s.tok_str(tok), pos: tok.start }
} else {
let name = s.expect_advance(T::Ident)?;
s.expect_advance(T::Colon)?;
CommentOr::Or(StructField {
pos: name.start,
name: s.tok_str(name),
ty: s.expr()?,
})
})
})
},
captured: {
self.ns_bound = prev_boundary;
let captured = &mut self.ctx.captured[prev_captured..];
crate::quad_sort(captured, core::cmp::Ord::cmp);
let preserved = captured.partition_dedup().0.len();
self.ctx.captured.truncate(prev_captured + preserved);
self.arena.alloc_slice(&self.ctx.captured[prev_captured..])
},
pos: {
if self.ns_bound == 0 {
// we might save some memory
self.ctx.captured.clear();
}
let name = s.expect_advance(T::Ident)?;
s.expect_advance(T::Colon)?;
let (ty, default_value) = match s.expr()? {
Expr::BinOp { left, op: T::Assign, right, .. } => (*left, Some(*right)),
ty => (ty, None),
};
Some(Some(StructField {
pos: name.start,
name: s.tok_str(name),
ty,
default_value,
}))
})?,
captured: self.collect_captures(prev_boundary, prev_captured),
trailing_comma: core::mem::take(&mut self.trailing_sep) || must_trail,
},
T::Union => E::Union {
pos,
fields: self.collect_fields(&mut must_trail, |s| {
if s.lexer.taste().kind != T::Colon {
return Some(None);
}
let name = s.expect_advance(T::Ident)?;
s.expect_advance(T::Colon)?;
Some(Some(UnionField { pos: name.start, name: s.tok_str(name), ty: s.expr()? }))
})?,
captured: self.collect_captures(prev_boundary, prev_captured),
trailing_comma: core::mem::take(&mut self.trailing_sep) || must_trail,
},
T::Enum => E::Enum {
pos,
variants: self.collect_fields(&mut must_trail, |s| {
if !matches!(s.lexer.taste().kind, T::Comma | T::RBrace) {
return Some(None);
}
let name = s.expect_advance(T::Ident)?;
Some(Some(EnumField { pos: name.start, name: s.tok_str(name) }))
})?,
captured: self.collect_captures(prev_boundary, prev_captured),
trailing_comma: core::mem::take(&mut self.trailing_sep) || must_trail,
pos
},
trailing_comma: core::mem::take(&mut self.trailing_sep),
},
T::Ident | T::CtIdent => {
let (id, is_first) = self.resolve_ident(token);
E::Ident {
pos: pos - (token.kind == T::CtIdent) as Pos,
is_ct: token.kind == T::CtIdent,
id,
is_first,
}
E::Ident { pos, is_ct: token.kind == T::CtIdent, id, is_first }
}
T::Under => E::Wildcard { pos },
T::If => E::If {
@ -439,22 +399,7 @@ impl<'a, 'b> Parser<'a, 'b> {
then: self.ptr_expr()?,
else_: self.advance_if(T::Else).then(|| self.ptr_expr()).trans()?,
},
T::Match => E::Match {
pos,
value: self.ptr_expr()?,
branches: {
self.expect_advance(T::LBrace)?;
self.collect_list(T::Comma, T::RBrace, |s| {
Some(MatchBranch {
pat: s.expr()?,
pos: s.expect_advance(T::TArrow)?.start,
body: s.expr()?,
})
})
},
},
T::Loop => E::Loop { pos, unrolled: false, body: self.ptr_expr()? },
T::CtLoop => E::Loop { pos, unrolled: true, body: self.ptr_expr()? },
T::Loop => E::Loop { pos, body: self.ptr_expr()? },
T::Break => E::Break { pos },
T::Continue => E::Continue { pos },
T::Return => E::Return {
@ -473,7 +418,7 @@ impl<'a, 'b> Parser<'a, 'b> {
self.collect_list(T::Comma, T::RParen, |s| {
let name = s.advance_ident()?;
let (id, _) = s.resolve_ident(name);
s.declare(name.start, id, true, true, name.kind == T::CtIdent);
s.declare(name.start, id, true, true);
s.expect_advance(T::Colon)?;
Some(Arg {
pos: name.start,
@ -491,33 +436,23 @@ impl<'a, 'b> Parser<'a, 'b> {
body: self.ptr_expr()?,
},
T::Ctor => self.ctor(pos, None),
T::Tupl => self.tupl(pos, None, ListKind::Tuple),
T::Arr => self.tupl(pos, None, ListKind::Array),
T::Tupl => self.tupl(pos, None),
T::LBrack => E::Slice {
size: {
if self.advance_if(T::RBrack) {
None
} else {
let adv = self.ptr_expr()?;
self.expect_advance(T::RBrack)?;
Some(adv)
}
item: self.ptr_unit_expr()?,
size: self.advance_if(T::Semi).then(|| self.ptr_expr()).trans()?,
pos: {
self.expect_advance(T::RBrack)?;
pos
},
item: self.arena.alloc(self.unit_expr_low(false)?),
pos,
},
T::Band | T::Mul | T::Xor | T::Sub | T::Que | T::Not | T::Dot => E::UnOp {
T::Band | T::Mul | T::Xor | T::Sub | T::Que => E::UnOp {
pos,
op: token.kind,
val: {
let prev_ident_stack = self.ctx.idents.len();
let expr = self.ptr_unit_expr()?;
if token.kind == T::Band {
self.flag_idents(*expr, idfl::REFERENCED);
}
if token.kind == T::Dot {
self.ctx.idents.truncate(prev_ident_stack);
}
expr
},
},
@ -556,100 +491,52 @@ impl<'a, 'b> Parser<'a, 'b> {
tok => self.report(token.start, format_args!("unexpected token: {tok}"))?,
};
if eat_tail {
loop {
let token = self.token;
if matches!(
token.kind,
T::LParen | T::Ctor | T::Dot | T::Tupl | T::Arr | T::LBrack | T::Colon
) {
self.next();
}
loop {
let token = self.token;
if matches!(token.kind, T::LParen | T::Ctor | T::Dot | T::Tupl | T::LBrack) {
self.next();
}
expr = match token.kind {
T::LParen => Expr::Call {
func: self.arena.alloc(expr),
args: self.collect_list(T::Comma, T::RParen, Self::expr),
trailing_comma: core::mem::take(&mut self.trailing_sep),
expr = match token.kind {
T::LParen => Expr::Call {
func: self.arena.alloc(expr),
args: self.collect_list(T::Comma, T::RParen, Self::expr),
trailing_comma: core::mem::take(&mut self.trailing_sep),
},
T::Ctor => self.ctor(token.start, Some(expr)),
T::Tupl => self.tupl(token.start, Some(expr)),
T::LBrack => E::Index {
base: self.arena.alloc(expr),
index: {
let index = self.expr()?;
self.expect_advance(T::RBrack)?;
self.arena.alloc(index)
},
T::Ctor => self.ctor(token.start, Some(expr)),
T::Tupl => self.tupl(token.start, Some(expr), ListKind::Tuple),
T::Arr => self.tupl(token.start, Some(expr), ListKind::Array),
T::LBrack => E::Index {
base: self.arena.alloc(expr),
index: self.arena.alloc({
if self.advance_if(T::Range) {
let pos = self.token.start;
if self.advance_if(T::RBrack) {
Expr::Range { pos, start: None, end: None }
} else {
let res = Expr::Range {
pos,
start: None,
end: Some(self.ptr_expr()?),
};
self.expect_advance(T::RBrack)?;
res
}
} else {
let start = self.expr()?;
let pos = self.token.start;
if self.advance_if(T::Range) {
let start = self.arena.alloc(start);
if self.advance_if(T::RBrack) {
Expr::Range { pos, start: Some(start), end: None }
} else {
let res = Expr::Range {
pos,
start: Some(start),
end: Some(self.ptr_expr()?),
};
self.expect_advance(T::RBrack)?;
res
}
} else {
self.expect_advance(T::RBrack)?;
start
}
}
}),
},
T::Dot => E::Field {
target: self.arena.alloc(expr),
pos: token.start,
name: {
let token = self.expect_advance(T::Ident)?;
self.tok_str(token)
},
T::Colon => E::BinOp {
left: {
self.declare_rec(&expr, false);
self.arena.alloc(expr)
},
pos,
op: T::Colon,
right: self.ptr_expr()?,
},
T::Dot => E::Field {
target: self.arena.alloc(expr),
pos: token.start,
name: {
let token = self.expect_advance(T::Ident)?;
self.tok_str(token)
},
},
_ => break,
}
},
_ => break,
}
}
if matches!(token.kind, T::Loop | T::LBrace | T::Fn | T::Struct) {
if matches!(token.kind, T::Loop | T::LBrace | T::Fn) {
self.pop_scope(frame);
}
Some(expr)
}
fn tupl(&mut self, pos: Pos, ty: Option<Expr<'a>>, kind: ListKind) -> Expr<'a> {
Expr::List {
fn tupl(&mut self, pos: Pos, ty: Option<Expr<'a>>) -> Expr<'a> {
Expr::Tupl {
pos,
kind,
ty: ty.map(|ty| self.arena.alloc(ty)),
fields: self.collect_list(TokenKind::Comma, kind.term(), Self::expr),
fields: self.collect_list(TokenKind::Comma, TokenKind::RParen, Self::expr),
trailing_comma: core::mem::take(&mut self.trailing_sep),
}
}
@ -676,49 +563,6 @@ impl<'a, 'b> Parser<'a, 'b> {
}
}
fn collect_fields<T: Copy>(
&mut self,
must_trail: &mut bool,
mut parse_field: impl FnMut(&mut Self) -> Option<Option<T>>,
) -> Option<FieldList<'a, T>> {
use TokenKind as T;
self.ns_bound = self.ctx.idents.len();
self.expect_advance(T::LBrace)?;
Some(self.collect_list(T::Comma, T::RBrace, |s| {
let tok = s.token;
Some(if s.advance_if(T::Comment) {
CommentOr::Comment { literal: s.tok_str(tok), pos: tok.start }
} else if let Some(field) = parse_field(s)? {
CommentOr::Or(Ok(field))
} else {
*must_trail = true;
CommentOr::Or(Err(
s.collect_list_low(T::Semi, T::RBrace, true, |s| s.expr_low(true))
))
})
}))
}
fn collect_captures(
&mut self,
prev_captured: usize,
prev_boundary: usize,
) -> &'a [CapturedIdent] {
self.ns_bound = prev_boundary;
let captured = &mut self.ctx.captured[prev_captured..];
crate::quad_sort(captured, core::cmp::Ord::cmp);
let preserved = captured.partition_dedup().0.len();
self.ctx.captured.truncate(prev_captured + preserved);
let slc = self.arena.alloc_slice(&self.ctx.captured[prev_captured..]);
if self.ns_bound == 0 {
// we might save some memory
self.ctx.captured.clear();
}
slc
}
fn advance_ident(&mut self) -> Option<Token> {
let next = self.next();
if matches!(next.kind, TokenKind::Ident | TokenKind::CtIdent) {
@ -734,8 +578,6 @@ impl<'a, 'b> Parser<'a, 'b> {
if !&self.ctx.idents[i].declared {
self.ctx.idents.swap(i, undeclared_count);
undeclared_count += 1;
} else if !self.ctx.idents[i].used {
self.warn(self.ctx.idents[i].ident.pos(), "unused identifier");
}
}
@ -754,23 +596,11 @@ impl<'a, 'b> Parser<'a, 'b> {
&mut self,
delim: TokenKind,
end: TokenKind,
f: impl FnMut(&mut Self) -> Option<T>,
) -> &'a [T] {
self.collect_list_low(delim, end, false, f)
}
fn collect_list_low<T: Copy>(
&mut self,
delim: TokenKind,
end: TokenKind,
keep_end: bool,
mut f: impl FnMut(&mut Self) -> Option<T>,
) -> &'a [T] {
let mut trailing_sep = false;
let mut view = self.ctx.stack.view();
'o: while (keep_end && self.token.kind != end)
|| (!keep_end && !self.advance_if(end)) && self.token.kind != TokenKind::Eof
{
'o: while !self.advance_if(end) {
let val = match f(self) {
Some(val) => val,
None => {
@ -827,25 +657,9 @@ impl<'a, 'b> Parser<'a, 'b> {
}
}
#[track_caller]
fn warn(&mut self, pos: Pos, msg: impl fmt::Display) {
if log::log_enabled!(log::Level::Error) {
use core::fmt::Write;
writeln!(
self.ctx.warnings.get_mut(),
"(W) {}",
Report::new(self.lexer.source(), self.path, pos, msg)
)
.unwrap();
}
}
#[track_caller]
fn report(&mut self, pos: Pos, msg: impl fmt::Display) -> Option<!> {
if log::log_enabled!(log::Level::Error) {
if self.ctx.errors.get_mut().len() > 1024 * 10 {
panic!("{}", self.ctx.errors.get_mut());
}
use core::fmt::Write;
writeln!(
self.ctx.errors.get_mut(),
@ -859,19 +673,15 @@ impl<'a, 'b> Parser<'a, 'b> {
fn flag_idents(&mut self, e: Expr<'a>, flags: IdentFlags) {
match e {
Expr::Ident { id, .. } => {
if let Some(f) = find_ident(&mut self.ctx.idents, id) {
f.flags |= flags;
}
}
Expr::Ident { id, .. } => find_ident(&mut self.ctx.idents, id).flags |= flags,
Expr::Field { target, .. } => self.flag_idents(*target, flags),
_ => {}
}
}
}
fn find_ident(idents: &mut [ScopeIdent], id: Ident) -> Option<&mut ScopeIdent> {
idents.binary_search_by_key(&id, |si| si.ident).map(|i| &mut idents[i]).ok()
fn find_ident(idents: &mut [ScopeIdent], id: Ident) -> &mut ScopeIdent {
idents.binary_search_by_key(&id, |si| si.ident).map(|i| &mut idents[i]).unwrap()
}
pub fn find_symbol(symbols: &[Symbol], id: Ident) -> &Symbol {
@ -945,32 +755,21 @@ pub enum Radix {
Decimal = 10,
}
pub type FieldList<'a, T> = &'a [CommentOr<'a, Result<T, &'a [Expr<'a>]>>];
generate_expr! {
/// `LIST(start, sep, end, elem) => start { elem sep } [elem] end`
/// `OP := grep for `#define OP:`
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Expr<'a> {
/// `'defer' Expr`
Defer {
/// `'ct' Expr`
Ct {
pos: Pos,
value: &'a Self,
},
/// `'Self'`
Slf {
pos: Pos,
},
/// `'"([^"]|\\")"'`
String {
pos: Pos,
literal: &'a str,
},
/// `'\'([^']|\\\')\''`
Char {
pos: Pos,
literal: &'a str,
},
/// `'//[^\n]' | '/*' { '([^/*]|*/)*' | Comment } '*/'
Comment {
pos: Pos,
@ -1045,15 +844,9 @@ generate_expr! {
then: &'a Self,
else_: Option<&'a Self>,
},
Match {
pos: Pos,
value: &'a Self,
branches: &'a [MatchBranch<'a>],
},
/// `'loop' Expr`
Loop {
pos: Pos,
unrolled: bool,
body: &'a Self,
},
/// `('&' | '*' | '^') Expr`
@ -1065,25 +858,11 @@ generate_expr! {
/// `'struct' LIST('{', ',', '}', Ident ':' Expr)`
Struct {
pos: Pos,
fields: FieldList<'a, StructField<'a>>,
captured: &'a [CapturedIdent],
fields: &'a [CommentOr<'a, StructField<'a>>],
captured: &'a [Ident],
trailing_comma: bool,
packed: bool,
},
/// `'union' LIST('{', ',', '}', Ident ':' Expr)`
Union {
pos: Pos,
fields: FieldList<'a, UnionField<'a>>,
captured: &'a [CapturedIdent],
trailing_comma: bool,
},
/// `'enum' LIST('{', ',', '}', Ident)`
Enum {
pos: Pos,
variants: FieldList<'a, EnumField<'a>>,
captured: &'a [CapturedIdent],
trailing_comma: bool,
},
/// `[Expr] LIST('.{', ',', '}', Ident [':' Expr])`
Ctor {
pos: Pos,
@ -1092,9 +871,8 @@ generate_expr! {
trailing_comma: bool,
},
/// `[Expr] LIST('.(', ',', ')', Ident [':' Expr])`
List {
Tupl {
pos: Pos,
kind: ListKind,
ty: Option<&'a Self>,
fields: &'a [Self],
trailing_comma: bool,
@ -1110,12 +888,6 @@ generate_expr! {
base: &'a Self,
index: &'a Self,
},
/// `[ Expr ] .. [ Expr ]`
Range {
pos: u32,
start: Option<&'a Self>,
end: Option<&'a Self>,
},
/// `Expr '.' Ident`
Field {
target: &'a Self,
@ -1149,44 +921,22 @@ generate_expr! {
/// `'@use' '(' String ')'`
Mod {
pos: Pos,
id: Module,
id: FileId,
path: &'a str,
},
/// `'@use' '(' String ')'`
Embed {
pos: Pos,
id: Global,
id: FileId,
path: &'a str,
},
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, PartialOrd, Ord)]
pub struct CapturedIdent {
pub id: Ident,
pub is_ct: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ListKind {
Tuple,
Array,
}
impl ListKind {
fn term(self) -> TokenKind {
match self {
ListKind::Tuple => TokenKind::RParen,
ListKind::Array => TokenKind::RBrack,
}
}
}
impl Expr<'_> {
pub fn declares(&self, iden: DeclId, source: &str) -> Option<Ident> {
pub fn declares(&self, iden: Result<Ident, &str>, source: &str) -> Option<Ident> {
match *self {
Self::Ident { id, .. }
if iden == DeclId::Ident(id) || iden == DeclId::Name(&source[id.range()]) =>
{
Self::Ident { id, .. } if iden == Ok(id) || iden == Err(&source[id.range()]) => {
Some(id)
}
Self::Ctor { fields, .. } => fields.iter().find_map(|f| f.value.declares(iden, source)),
@ -1202,14 +952,14 @@ impl Expr<'_> {
}
}
pub fn find_pattern_path<T, F: FnOnce(&Expr, bool) -> T>(
pub fn find_pattern_path<T, F: FnOnce(&Expr) -> T>(
&self,
ident: Ident,
target: &Expr,
mut with_final: F,
) -> Result<T, F> {
match *self {
Self::Ident { id, is_ct, .. } if id == ident => Ok(with_final(target, is_ct)),
Self::Ident { id, .. } if id == ident => Ok(with_final(target)),
Self::Ctor { fields, .. } => {
for &CtorField { name, value, pos } in fields {
match value.find_pattern_path(
@ -1228,50 +978,11 @@ impl Expr<'_> {
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct MatchBranch<'a> {
pub pat: Expr<'a>,
pub pos: Pos,
pub body: Expr<'a>,
}
impl Poser for MatchBranch<'_> {
fn posi(&self) -> Pos {
self.pat.pos()
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct EnumField<'a> {
pub pos: Pos,
pub name: &'a str,
}
impl Poser for EnumField<'_> {
fn posi(&self) -> Pos {
self.pos
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct UnionField<'a> {
pub pos: Pos,
pub name: &'a str,
pub ty: Expr<'a>,
}
impl Poser for UnionField<'_> {
fn posi(&self) -> Pos {
self.pos
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct StructField<'a> {
pub pos: Pos,
pub name: &'a str,
pub ty: Expr<'a>,
pub default_value: Option<Expr<'a>>,
}
impl Poser for StructField<'_> {
@ -1297,18 +1008,6 @@ pub trait Poser {
fn posi(&self) -> Pos;
}
impl<O: Poser, E: Poser> Poser for Result<O, E> {
fn posi(&self) -> Pos {
self.as_ref().map_or_else(Poser::posi, Poser::posi)
}
}
impl<T: Poser> Poser for &[T] {
fn posi(&self) -> Pos {
self[0].posi()
}
}
impl Poser for Pos {
fn posi(&self) -> Pos {
*self
@ -1336,9 +1035,9 @@ pub enum CommentOr<'a, T> {
Comment { literal: &'a str, pos: Pos },
}
impl<T> CommentOr<'_, T> {
pub fn or(&self) -> Option<&T> {
match self {
impl<T: Copy> CommentOr<'_, T> {
pub fn or(&self) -> Option<T> {
match *self {
CommentOr::Or(v) => Some(v),
CommentOr::Comment { .. } => None,
}
@ -1365,11 +1064,10 @@ impl core::fmt::Display for Display<'_> {
#[derive(Default)]
pub struct Ctx {
pub errors: RefCell<String>,
pub warnings: RefCell<String>,
symbols: Vec<Symbol>,
stack: StackAlloc,
idents: Vec<ScopeIdent>,
captured: Vec<CapturedIdent>,
captured: Vec<Ident>,
}
impl Ctx {
@ -1508,8 +1206,13 @@ impl Ast {
unsafe { self.0.as_ref() }
}
pub fn find_decl(&self, id: DeclId) -> Option<(&Expr, Ident)> {
find_decl(self.exprs(), &self.file, id)
pub fn find_decl(&self, id: Result<Ident, &str>) -> Option<(&Expr, Ident)> {
self.exprs().iter().find_map(|expr| match expr {
Expr::BinOp { left, op: TokenKind::Decl, .. } => {
left.declares(id, &self.file).map(|id| (expr, id))
}
_ => None,
})
}
pub fn ident_str(&self, ident: Ident) -> &str {
@ -1517,44 +1220,13 @@ impl Ast {
}
}
pub fn find_decl<'a>(
exprs: &'a [Expr<'a>],
file: &str,
id: DeclId,
) -> Option<(&'a Expr<'a>, Ident)> {
exprs.iter().find_map(|expr| match expr {
Expr::BinOp { left, op: TokenKind::Decl | TokenKind::Colon, .. } => {
left.declares(id, file).map(|id| (expr, id))
}
_ => None,
})
}
#[derive(PartialEq, Eq, Clone, Copy)]
pub enum DeclId<'a> {
Ident(Ident),
Name(&'a str),
}
impl From<Ident> for DeclId<'_> {
fn from(value: Ident) -> Self {
Self::Ident(value)
}
}
impl<'a> From<&'a str> for DeclId<'a> {
fn from(value: &'a str) -> Self {
Self::Name(value)
}
}
impl Default for Ast {
fn default() -> Self {
Self(AstInner::new("".into(), "".into(), &mut Ctx::default(), &mut no_loader))
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[derive(Clone, Copy)]
#[repr(packed)]
pub struct ExprRef(NonNull<Expr<'static>>);
@ -1820,7 +1492,7 @@ impl ArenaChunk {
fn contains(&self, arg: *mut u8) -> bool {
(self.base <= arg && unsafe { self.base.add(self.size) } > arg)
|| self.next().is_some_and(|s| s.contains(arg))
|| self.next().map_or(false, |s| s.contains(arg))
}
pub fn size(&self) -> usize {

File diff suppressed because it is too large Load diff

View file

@ -1,66 +1,19 @@
use {
super::{AssemblySpec, Backend},
super::{AssemblySpec, Backend, Nid, Node, Nodes},
crate::{
lexer::TokenKind,
nodes::{Kind, Nid, Nodes, MEM},
parser,
ty::{self, Loc, Module, Offset, Size, Types},
utils::{EntSlice, EntVec},
parser, reg,
son::{debug_assert_matches, write_reloc, Kind, MEM},
ty::{self, Loc},
Offset, Reloc, Size, TypedReloc, Types,
},
alloc::{boxed::Box, collections::BTreeMap, string::String, vec::Vec},
core::{assert_matches::debug_assert_matches, mem, ops::Range},
core::mem,
hbbytecode::{self as instrs, *},
reg::Reg,
};
mod regalloc;
mod reg {
pub const STACK_PTR: Reg = 254;
pub const ZERO: Reg = 0;
pub const RET: Reg = 1;
pub const RET_ADDR: Reg = 31;
pub type Reg = u8;
}
fn write_reloc(doce: &mut [u8], offset: usize, value: i64, size: u16) {
let value = value.to_ne_bytes();
doce[offset..offset + size as usize].copy_from_slice(&value[..size as usize]);
}
#[derive(Clone, Copy)]
struct TypedReloc {
target: ty::Id,
reloc: Reloc,
}
// TODO: make into bit struct (width: u2, sub_offset: u3, offset: u27)
#[derive(Clone, Copy, Debug)]
struct Reloc {
offset: Offset,
sub_offset: u8,
width: u8,
}
impl Reloc {
fn new(offset: usize, sub_offset: u8, width: u8) -> Self {
Self { offset: offset as u32, sub_offset, width }
}
fn apply_jump(mut self, code: &mut [u8], to: u32, from: u32) -> i64 {
self.offset += from;
let offset = to as i64 - self.offset as i64;
self.write_offset(code, offset);
offset
}
fn write_offset(&self, code: &mut [u8], offset: i64) {
let bytes = offset.to_ne_bytes();
let slice = &mut code[self.offset as usize + self.sub_offset as usize..];
slice[..self.width as usize].copy_from_slice(&bytes[..self.width as usize]);
}
}
mod my_regalloc;
mod their_regalloc;
struct FuncDt {
offset: Offset,
@ -94,10 +47,11 @@ struct Assembler {
#[derive(Default)]
pub struct HbvmBackend {
funcs: EntVec<ty::Func, FuncDt>,
globals: EntVec<ty::Global, GlobalDt>,
funcs: Vec<FuncDt>,
globals: Vec<GlobalDt>,
asm: Assembler,
ralloc: regalloc::Res,
ralloc: their_regalloc::Regalloc,
ralloc_my: my_regalloc::Res,
ret_relocs: Vec<Reloc>,
relocs: Vec<TypedReloc>,
@ -144,13 +98,13 @@ impl Backend for HbvmBackend {
debug_assert!(self.asm.funcs.is_empty());
debug_assert!(self.asm.globals.is_empty());
self.globals.shadow(types.ins.globals.len());
self.globals.resize_with(types.ins.globals.len(), Default::default);
self.asm.frontier.push(from.into());
self.asm.frontier.push(ty::Kind::Func(from).compress());
while let Some(itm) = self.asm.frontier.pop() {
match itm.expand() {
ty::Kind::Func(func) => {
let fuc = &mut self.funcs[func];
let fuc = &mut self.funcs[func as usize];
debug_assert!(!fuc.code.is_empty());
if fuc.offset != u32::MAX {
continue;
@ -160,7 +114,7 @@ impl Backend for HbvmBackend {
self.asm.frontier.extend(fuc.relocs.iter().map(|r| r.target));
}
ty::Kind::Global(glob) => {
let glb = &mut self.globals[glob];
let glb = &mut self.globals[glob as usize];
if glb.offset != u32::MAX {
continue;
}
@ -174,7 +128,7 @@ impl Backend for HbvmBackend {
let init_len = to.len();
for &func in &self.asm.funcs {
let fuc = &mut self.funcs[func];
let fuc = &mut self.funcs[func as usize];
fuc.offset = to.len() as _;
debug_assert!(!fuc.code.is_empty());
to.extend(&fuc.code);
@ -183,18 +137,18 @@ impl Backend for HbvmBackend {
let code_length = to.len() - init_len;
for global in self.asm.globals.drain(..) {
self.globals[global].offset = to.len() as _;
to.extend(&types.ins.globals[global].data);
self.globals[global as usize].offset = to.len() as _;
to.extend(&types.ins.globals[global as usize].data);
}
let data_length = to.len() - code_length - init_len;
for func in self.asm.funcs.drain(..) {
let fuc = &self.funcs[func];
let fuc = &self.funcs[func as usize];
for rel in &fuc.relocs {
let offset = match rel.target.expand() {
ty::Kind::Func(fun) => self.funcs[fun].offset,
ty::Kind::Global(glo) => self.globals[glo].offset,
ty::Kind::Func(fun) => self.funcs[fun as usize].offset,
ty::Kind::Global(glo) => self.globals[glo as usize].offset,
_ => unreachable!(),
};
rel.reloc.apply_jump(to, offset, fuc.offset);
@ -204,7 +158,7 @@ impl Backend for HbvmBackend {
AssemblySpec {
code_length: code_length as _,
data_length: data_length as _,
entry: self.funcs[from].offset,
entry: self.funcs[from as usize].offset,
}
}
@ -213,7 +167,7 @@ impl Backend for HbvmBackend {
mut sluce: &[u8],
eca_handler: &mut dyn FnMut(&mut &[u8]),
types: &'a Types,
files: &'a EntSlice<Module, parser::Ast>,
files: &'a [parser::Ast],
output: &mut String,
) -> Result<(), hbbytecode::DisasmError<'a>> {
use hbbytecode::DisasmItem;
@ -221,11 +175,11 @@ impl Backend for HbvmBackend {
.ins
.funcs
.iter()
.zip(self.funcs.iter())
.zip(&self.funcs)
.filter(|(_, f)| f.offset != u32::MAX)
.map(|(f, fd)| {
let name = if f.file != Module::default() {
let file = &files[f.file];
let name = if f.file != u32::MAX {
let file = &files[f.file as usize];
file.ident_str(f.name)
} else {
"target_fn"
@ -237,13 +191,13 @@ impl Backend for HbvmBackend {
.ins
.globals
.iter()
.zip(self.globals.iter())
.zip(&self.globals)
.filter(|(_, g)| g.offset != u32::MAX)
.map(|(g, gd)| {
let name = if g.file == Module::default() {
let name = if g.file == u32::MAX {
core::str::from_utf8(&g.data).unwrap_or("invalid utf-8")
} else {
let file = &files[g.file];
let file = &files[g.file as usize];
file.ident_str(g.name)
};
(gd.offset, (name, g.data.len() as Size, DisasmItem::Global))
@ -256,43 +210,32 @@ impl Backend for HbvmBackend {
fn emit_ct_body(
&mut self,
id: ty::Func,
nodes: &Nodes,
nodes: &mut Nodes,
tys: &Types,
files: &EntSlice<Module, parser::Ast>,
files: &[parser::Ast],
) {
self.emit_body(id, nodes, tys, files);
let fd = &mut self.funcs[id];
let fd = &mut self.funcs[id as usize];
fd.code.truncate(fd.code.len() - instrs::jala(0, 0, 0).0);
emit(&mut fd.code, instrs::tx());
}
fn emit_body(
&mut self,
id: ty::Func,
nodes: &Nodes,
tys: &Types,
files: &EntSlice<Module, parser::Ast>,
) {
let sig = tys.ins.funcs[id].sig;
fn emit_body(&mut self, id: ty::Func, nodes: &mut Nodes, tys: &Types, files: &[parser::Ast]) {
let sig = tys.ins.funcs[id as usize].sig.unwrap();
debug_assert!(self.code.is_empty());
self.offsets.clear();
self.offsets.resize(nodes.len(), Offset::MAX);
self.offsets.resize(nodes.values.len(), Offset::MAX);
let mut stack_size = 0;
'_compute_stack: {
let mems = &nodes[MEM].outputs;
let mems = mem::take(&mut nodes[MEM].outputs);
for &stck in mems.iter() {
if !matches!(nodes[stck].kind, Kind::Stck | Kind::Arg) {
debug_assert_matches!(
nodes[stck].kind,
Kind::Phi
| Kind::Return { .. }
| Kind::Load
| Kind::Call { .. }
| Kind::Stre
| Kind::Join
Kind::Phi | Kind::Return | Kind::Load | Kind::Call { .. } | Kind::Stre
);
continue;
}
@ -305,23 +248,27 @@ impl Backend for HbvmBackend {
}
self.offsets[stck as usize] = stack_size - self.offsets[stck as usize];
}
nodes[MEM].outputs = mems;
}
let (saved, tail) = self.emit_body_code(nodes, sig, tys, files);
//let (saved, tail) = self.emit_body_code_my(nodes, sig, tys, files);
if let Some(last_ret) = self.ret_relocs.last()
&& last_ret.offset as usize == self.code.len() - 5
&& self
.jump_relocs
.last()
.is_none_or(|&(r, _)| self.offsets[r as usize] as usize != self.code.len())
.map_or(true, |&(r, _)| self.offsets[r as usize] as usize != self.code.len())
{
self.code.truncate(self.code.len() - 5);
self.ret_relocs.pop();
}
// FIXME: maybe do this incrementally
for (nd, rel) in self.jump_relocs.drain(..) {
let offset = self.offsets[nd as usize];
//debug_assert!(offset < self.code.len() as u32 - 1);
rel.apply_jump(&mut self.code, offset, 0);
}
@ -372,9 +319,11 @@ impl Backend for HbvmBackend {
self.emit(instrs::jala(reg::ZERO, reg::RET_ADDR, 0));
}
self.funcs.shadow(tys.ins.funcs.len());
self.funcs[id].code = mem::take(&mut self.code);
self.funcs[id].relocs = mem::take(&mut self.relocs);
if self.funcs.get(id as usize).is_none() {
self.funcs.resize_with(id as usize + 1, Default::default);
}
self.funcs[id as usize].code = mem::take(&mut self.code);
self.funcs[id as usize].relocs = mem::take(&mut self.relocs);
debug_assert_eq!(self.ret_relocs.len(), 0);
debug_assert_eq!(self.relocs.len(), 0);
@ -384,52 +333,27 @@ impl Backend for HbvmBackend {
}
impl Nodes {
fn cond_op(&self, cnd: Nid) -> CondRet {
let Kind::BinOp { op } = self[cnd].kind else { return None };
if self.is_unlocked(cnd) {
return None;
}
op.cond_op(self[self[cnd].inputs[1]].ty)
}
fn strip_offset(&self, region: Nid) -> (Nid, Offset) {
if matches!(self[region].kind, Kind::BinOp { op: TokenKind::Add | TokenKind::Sub })
&& self.is_locked(region)
&& let Kind::CInt { value } = self[self[region].inputs[2]].kind
{
(self[region].inputs[1], value as _)
} else {
(region, 0)
}
}
fn is_never_used(&self, nid: Nid, tys: &Types) -> bool {
let node = &self[nid];
match node.kind {
Kind::CInt { value: 0 } => false,
Kind::CInt { value: 1.. } => node.outputs.iter().all(|&o| {
Kind::CInt { .. } => node.outputs.iter().all(|&o| {
matches!(self[o].kind, Kind::BinOp { op }
if op.imm_binop(self[o].ty).is_some()
&& self.is_const(self[o].inputs[2])
&& op.cond_op(self[o].ty).is_none())
}),
Kind::BinOp { op: TokenKind::Mul } if node.ty.is_float() => {
node.outputs.iter().all(|&n| {
self[n].kind == Kind::BinOp { op: TokenKind::Add } && self[n].inputs[1] == nid
})
}
Kind::BinOp { op: TokenKind::Add | TokenKind::Sub } => {
(self.is_locked(node.inputs[1]) && !self[node.inputs[1]].ty.is_float())
self[node.inputs[1]].lock_rc != 0
|| (self.is_const(node.inputs[2])
&& node.outputs.iter().all(|&n| self.uses_direct_offset_of(n, nid, tys)))
&& node.outputs.iter().all(|&n| self[n].uses_direct_offset_of(nid, tys)))
}
Kind::BinOp { op } => {
op.cond_op(self[node.inputs[1]].ty).is_some()
op.cond_op(node.ty).is_some()
&& node.outputs.iter().all(|&n| self[n].kind == Kind::If)
}
Kind::Stck if tys.size_of(node.ty) == 0 => true,
Kind::Stck | Kind::Arg => node.outputs.iter().all(|&n| {
self.uses_direct_offset_of(n, nid, tys)
self[n].uses_direct_offset_of(nid, tys)
|| (matches!(self[n].kind, Kind::BinOp { op: TokenKind::Add })
&& self.is_never_used(n, tys))
}),
@ -437,59 +361,22 @@ impl Nodes {
_ => false,
}
}
fn uses_direct_offset_of(&self, user: Nid, target: Nid, tys: &Types) -> bool {
let node = &self[user];
((node.kind == Kind::Stre && node.inputs[2] == target)
|| (node.kind == Kind::Load && node.inputs[1] == target))
&& (node.ty.loc(tys) == Loc::Reg
// this means the struct is actually loaded into a register so no BMC needed
|| (node.kind == Kind::Load
&& !matches!(tys.parama(node.ty).0, Some(PLoc::Ref(..)))
&& node.outputs.iter().all(|&o| matches!(self[o].kind, Kind::Call { .. } | Kind::Return { .. }))))
}
}
impl HbvmBackend {
fn extend(
&mut self,
base: ty::Id,
dest: ty::Id,
reg: Reg,
tys: &Types,
files: &EntSlice<Module, parser::Ast>,
) {
if reg == 0 {
return;
}
let (bsize, dsize) = (tys.size_of(base), tys.size_of(dest));
debug_assert!(bsize <= 8, "{}", ty::Display::new(tys, files, base));
debug_assert!(dsize <= 8, "{}", ty::Display::new(tys, files, dest));
if bsize == dsize {
return Default::default();
}
self.emit(match (base.is_signed(), dest.is_signed()) {
(true, true) => {
let op = [instrs::sxt8, instrs::sxt16, instrs::sxt32][bsize.ilog2() as usize];
op(reg, reg)
}
_ => {
let mask = (1u64 << (bsize * 8)) - 1;
instrs::andi(reg, reg, mask)
}
});
impl Node {
fn uses_direct_offset_of(&self, nid: Nid, tys: &Types) -> bool {
((self.kind == Kind::Stre && self.inputs[2] == nid)
|| (self.kind == Kind::Load && self.inputs[1] == nid))
&& self.ty.loc(tys) == Loc::Reg
}
}
type CondRet = Option<(fn(u8, u8, i16) -> EncodedInstr, bool)>;
impl TokenKind {
fn cmp_against(self) -> Option<u64> {
Some(match self {
Self::Le | Self::Gt => 1,
Self::Ne | Self::Eq => 0,
Self::Ge | Self::Lt => (-1i64) as _,
TokenKind::Le | TokenKind::Gt => 1,
TokenKind::Ne | TokenKind::Eq => 0,
TokenKind::Ge | TokenKind::Lt => (-1i64) as _,
_ => return None,
})
}
@ -501,21 +388,22 @@ impl TokenKind {
let size = ty.simple_size().unwrap();
let ops = match self {
Self::Gt => [instrs::fcmpgt32, instrs::fcmpgt64],
Self::Lt => [instrs::fcmplt32, instrs::fcmplt64],
TokenKind::Gt => [instrs::fcmpgt32, instrs::fcmpgt64],
TokenKind::Lt => [instrs::fcmplt32, instrs::fcmplt64],
_ => return None,
};
Some(ops[size.ilog2() as usize - 2])
}
fn cond_op(self, ty: ty::Id) -> CondRet {
#[expect(clippy::type_complexity)]
fn cond_op(self, ty: ty::Id) -> Option<(fn(u8, u8, i16) -> EncodedInstr, bool)> {
if ty.is_float() {
return None;
}
let signed = ty.is_signed();
Some((
match self {
Self::Eq => instrs::jne,
Self::Ne => instrs::jeq,
_ if ty.is_float() => return None,
Self::Le if signed => instrs::jgts,
Self::Le => instrs::jgtu,
Self::Lt if signed => instrs::jlts,
@ -524,14 +412,16 @@ impl TokenKind {
Self::Ge => instrs::jltu,
Self::Gt if signed => instrs::jgts,
Self::Gt => instrs::jgtu,
Self::Eq => instrs::jne,
Self::Ne => instrs::jeq,
_ => return None,
},
matches!(self, Self::Lt | Self::Gt),
matches!(self, Self::Lt | TokenKind::Gt),
))
}
fn binop(self, ty: ty::Id) -> Option<fn(u8, u8, u8) -> EncodedInstr> {
let size = ty.simple_size().unwrap_or_else(|| panic!("{:?}", ty.expand()));
let size = ty.simple_size().unwrap();
if ty.is_integer() || ty == ty::Id::BOOL || ty.is_pointer() {
macro_rules! div { ($($op:ident),*) => {[$(|a, b, c| $op(a, 0, b, c)),*]}; }
macro_rules! rem { ($($op:ident),*) => {[$(|a, b, c| $op(0, a, b, c)),*]}; }
@ -596,7 +486,7 @@ impl TokenKind {
Self::Band => return Some(andi),
Self::Bor => return Some(ori),
Self::Xor => return Some(xori),
Self::Shr if signed => basic_op!(srsi8, srsi16, srsi32, srsi64),
Self::Shr if signed => basic_op!(srui8, srui16, srui32, srui64),
Self::Shr => basic_op!(srui8, srui16, srui32, srui64),
Self::Shl => basic_op!(slui8, slui16, slui32, slui64),
_ => return None,
@ -606,84 +496,25 @@ impl TokenKind {
Some(ops[size.ilog2() as usize])
}
fn unop(&self, dst: ty::Id, src: ty::Id, tys: &Types) -> Option<fn(u8, u8) -> EncodedInstr> {
let src_idx = tys.size_of(src).ilog2() as usize;
fn unop(&self, dst: ty::Id, src: ty::Id) -> Option<fn(u8, u8) -> EncodedInstr> {
let src_idx = src.simple_size().unwrap().ilog2() as usize - 2;
Some(match self {
Self::Sub => [
|a, b| sub8(a, reg::ZERO, b),
|a, b| sub16(a, reg::ZERO, b),
|a, b| sub32(a, reg::ZERO, b),
|a, b| sub64(a, reg::ZERO, b),
][src_idx],
Self::Not => instrs::not,
Self::Sub => instrs::neg,
Self::Float if dst.is_float() && src.is_integer() => {
debug_assert_matches!(
(dst.simple_size(), src.simple_size()),
(Some(4 | 8), Some(8))
);
[instrs::itf32, instrs::itf64][dst.simple_size().unwrap().ilog2() as usize - 2]
debug_assert_eq!(dst.simple_size(), src.simple_size());
[instrs::itf32, instrs::itf64][src_idx]
}
Self::Number if src.is_float() && dst.is_integer() => {
[|a, b| instrs::fti32(a, b, 1), |a, b| instrs::fti64(a, b, 1)][src_idx - 2]
}
Self::Number if src.is_signed() && (dst.is_integer() || dst.is_pointer()) => {
[instrs::sxt8, instrs::sxt16, instrs::sxt32][src_idx]
}
Self::Number
if (src.is_unsigned() || src == ty::Id::BOOL)
&& (dst.is_integer() || dst.is_pointer()) =>
{
[
|a, b| instrs::andi(a, b, 0xff),
|a, b| instrs::andi(a, b, 0xffff),
|a, b| instrs::andi(a, b, 0xffffffff),
][src_idx]
[|a, b| instrs::fti32(a, b, 1), |a, b| instrs::fti64(a, b, 1)][src_idx]
}
Self::Float if dst.is_float() && src.is_float() => {
[instrs::fc32t64, |a, b| instrs::fc64t32(a, b, 1)][src_idx - 2]
[instrs::fc32t64, |a, b| instrs::fc64t32(a, b, 1)][src_idx]
}
_ => return None,
})
}
}
#[derive(Clone, Copy, Debug)]
enum PLoc {
Reg(Reg, u16),
WideReg(Reg, u16),
Ref(Reg, u32),
}
impl PLoc {
fn reg(self) -> u8 {
match self {
PLoc::Reg(r, _) | PLoc::WideReg(r, _) | PLoc::Ref(r, _) => r,
}
}
}
struct ParamAlloc(Range<Reg>);
impl ParamAlloc {
pub fn next(&mut self, ty: ty::Id, tys: &Types) -> Option<PLoc> {
Some(match tys.size_of(ty) {
0 => return None,
size @ 1..=8 => PLoc::Reg(self.0.next().unwrap(), size as _),
size @ 9..=16 => PLoc::WideReg(self.0.next_chunk::<2>().unwrap()[0], size as _),
size @ 17.. => PLoc::Ref(self.0.next().unwrap(), size),
})
}
}
impl Types {
fn parama(&self, ret: ty::Id) -> (Option<PLoc>, ParamAlloc) {
let mut iter = ParamAlloc(1..12);
let ret = iter.next(ret, self);
iter.0.start += ret.is_none() as u8;
(ret, iter)
}
}
type EncodedInstr = (usize, [u8; instrs::MAX_SIZE]);
fn emit(out: &mut Vec<u8>, (len, instr): EncodedInstr) {
out.extend_from_slice(&instr[..len]);
@ -697,7 +528,42 @@ fn binary_prelude(to: &mut Vec<u8>) {
#[derive(Default)]
pub struct LoggedMem {
pub mem: hbvm::mem::HostMemory,
logger: hbvm::mem::InstrLogger,
op_buf: Vec<hbbytecode::Oper>,
disp_buf: String,
prev_instr: Option<hbbytecode::Instr>,
}
impl LoggedMem {
unsafe fn display_instr<T>(&mut self, instr: hbbytecode::Instr, addr: hbvm::mem::Address) {
let novm: *const hbvm::Vm<Self, 0> = core::ptr::null();
let offset = core::ptr::addr_of!((*novm).memory) as usize;
let regs = unsafe {
&*core::ptr::addr_of!(
(*(((self as *mut _ as *mut u8).sub(offset)) as *const hbvm::Vm<Self, 0>))
.registers
)
};
let mut bytes = core::slice::from_raw_parts(
(addr.get() - 1) as *const u8,
core::mem::size_of::<T>() + 1,
);
use core::fmt::Write;
hbbytecode::parse_args(&mut bytes, instr, &mut self.op_buf).unwrap();
debug_assert!(bytes.is_empty());
self.disp_buf.clear();
write!(self.disp_buf, "{:<10}", format!("{instr:?}")).unwrap();
for (i, op) in self.op_buf.drain(..).enumerate() {
if i != 0 {
write!(self.disp_buf, ", ").unwrap();
}
write!(self.disp_buf, "{op:?}").unwrap();
if let hbbytecode::Oper::R(r) = op {
write!(self.disp_buf, "({})", regs[r as usize].0).unwrap()
}
}
log::trace!("read-typed: {:x}: {}", addr.get(), self.disp_buf);
}
}
impl hbvm::mem::Memory for LoggedMem {
@ -730,13 +596,19 @@ impl hbvm::mem::Memory for LoggedMem {
}
unsafe fn prog_read<T: Copy + 'static>(&mut self, addr: hbvm::mem::Address) -> T {
self.mem.prog_read(addr)
}
if log::log_enabled!(log::Level::Trace) {
if core::any::TypeId::of::<u8>() == core::any::TypeId::of::<T>() {
if let Some(instr) = self.prev_instr {
self.display_instr::<()>(instr, addr);
}
self.prev_instr = hbbytecode::Instr::try_from(*(addr.get() as *const u8)).ok();
} else {
let instr = self.prev_instr.take().unwrap();
self.display_instr::<T>(instr, addr);
}
}
fn log_instr(&mut self, at: hbvm::mem::Address, regs: &[hbvm::value::Value]) {
log::trace!("read-typed: {:x}: {}", at.get(), unsafe {
self.logger.display_instr(at, regs)
});
self.mem.prog_read(addr)
}
}
@ -843,7 +715,7 @@ pub struct AbleOsExecutableHeader {
#[cfg(test)]
pub fn test_run_vm(out: &[u8], output: &mut String) {
use core::{ffi::CStr, fmt::Write};
use core::fmt::Write;
let mut stack = [0_u64; 1024 * 20];
@ -860,12 +732,6 @@ pub fn test_run_vm(out: &[u8], output: &mut String) {
match vm.run() {
Ok(hbvm::VmRunOk::End) => break Ok(()),
Ok(hbvm::VmRunOk::Ecall) => match vm.read_reg(2).0 {
37 => writeln!(
output,
"{}",
unsafe { CStr::from_ptr(vm.read_reg(3).0 as _) }.to_str().unwrap()
)
.unwrap(),
1 => writeln!(output, "ev: Ecall").unwrap(), // compatibility with a test
69 => {
let [size, align] = [vm.read_reg(3).0 as usize, vm.read_reg(4).0 as usize];

View file

@ -0,0 +1,903 @@
use {
super::{HbvmBackend, Nid, Nodes},
crate::{
lexer::TokenKind,
parser,
reg::{self, Reg},
son::{debug_assert_matches, Kind, ARG_START, MEM, VOID},
ty::{self, Arg, Loc},
utils::{BitSet, Vc},
Offset, PLoc, Reloc, Sig, TypedReloc, Types,
},
alloc::{borrow::ToOwned, vec::Vec},
core::{mem, ops::Range},
hbbytecode::{self as instrs},
};
impl HbvmBackend {
pub fn emit_body_code_my(
&mut self,
nodes: &mut Nodes,
sig: Sig,
tys: &Types,
files: &[parser::Ast],
) -> (usize, bool) {
let mut fuc = Function::new(nodes, tys, sig);
log::info!("{fuc:?}");
let mut res = mem::take(&mut self.ralloc_my);
Env::new(&fuc, &fuc.func, &mut res).run();
'_open_function: {
self.emit(instrs::addi64(reg::STACK_PTR, reg::STACK_PTR, 0));
self.emit(instrs::st(reg::RET_ADDR + fuc.tail as u8, reg::STACK_PTR, 0, 0));
}
let reg_offset = if fuc.tail { reg::RET + 12 } else { reg::RET_ADDR + 1 };
res.node_to_reg.iter_mut().filter(|r| **r != 0).for_each(|r| {
*r += reg_offset - 1;
if fuc.tail && *r >= reg::RET_ADDR {
*r += 1;
}
});
let atr = |allc: Nid| res.node_to_reg[allc as usize];
//for (id, node) in fuc.nodes.iter() {
// if node.kind == Kind::Phi {
// debug_assert_eq!(atr(node.inputs[1]), atr(node.inputs[2]));
// debug_assert_eq!(atr(id), atr(node.inputs[2]));
// }
//}
let (retl, mut parama) = tys.parama(sig.ret);
let mut typs = sig.args.args();
let mut args = fuc.nodes[VOID].outputs[ARG_START..].iter();
while let Some(aty) = typs.next(tys) {
let Arg::Value(ty) = aty else { continue };
let Some(loc) = parama.next(ty, tys) else { continue };
let &arg = args.next().unwrap();
let (rg, size) = match loc {
PLoc::WideReg(rg, size) => (rg, size),
PLoc::Reg(rg, size) if ty.loc(tys) == Loc::Stack => (rg, size),
PLoc::Reg(r, ..) | PLoc::Ref(r, ..) => {
self.emit(instrs::cp(atr(arg), r));
continue;
}
};
self.emit(instrs::st(rg, reg::STACK_PTR, self.offsets[arg as usize] as _, size));
if fuc.nodes[arg].lock_rc == 0 {
self.emit(instrs::addi64(rg, reg::STACK_PTR, self.offsets[arg as usize] as _));
}
self.emit(instrs::cp(atr(arg), rg));
}
for (i, block) in fuc.func.blocks.iter().enumerate() {
self.offsets[block.entry as usize] = self.code.len() as _;
for &nid in &fuc.func.instrs[block.range.clone()] {
if nid == VOID {
continue;
}
let node = &fuc.nodes[nid];
let extend = |base: ty::Id, dest: ty::Id, from: Nid, to: Nid| {
let (bsize, dsize) = (tys.size_of(base), tys.size_of(dest));
debug_assert!(bsize <= 8, "{}", ty::Display::new(tys, files, base));
debug_assert!(dsize <= 8, "{}", ty::Display::new(tys, files, dest));
if bsize == dsize {
return Default::default();
}
match (base.is_signed(), dest.is_signed()) {
(true, true) => {
let op = [instrs::sxt8, instrs::sxt16, instrs::sxt32]
[bsize.ilog2() as usize];
op(atr(to), atr(from))
}
_ => {
let mask = (1u64 << (bsize * 8)) - 1;
instrs::andi(atr(to), atr(from), mask)
}
}
};
match node.kind {
Kind::If => {
let &[_, cnd] = node.inputs.as_slice() else { unreachable!() };
if let Kind::BinOp { op } = fuc.nodes[cnd].kind
&& let Some((op, swapped)) =
op.cond_op(fuc.nodes[fuc.nodes[cnd].inputs[1]].ty)
{
let &[_, lhs, rhs] = fuc.nodes[cnd].inputs.as_slice() else {
unreachable!()
};
self.emit(extend(fuc.nodes[lhs].ty, fuc.nodes[lhs].ty.extend(), 0, 0));
self.emit(extend(fuc.nodes[rhs].ty, fuc.nodes[rhs].ty.extend(), 1, 1));
let rel = Reloc::new(self.code.len(), 3, 2);
self.jump_relocs.push((node.outputs[!swapped as usize], rel));
self.emit(op(atr(lhs), atr(rhs), 0));
} else {
self.emit(extend(fuc.nodes[cnd].ty, fuc.nodes[cnd].ty.extend(), 0, 0));
let rel = Reloc::new(self.code.len(), 3, 2);
self.jump_relocs.push((node.outputs[0], rel));
self.emit(instrs::jne(atr(cnd), reg::ZERO, 0));
}
}
Kind::Loop | Kind::Region => {
if (mem::replace(&mut fuc.backrefs[nid as usize], u16::MAX) != u16::MAX)
^ (node.kind == Kind::Loop)
{
let index = (node.kind == Kind::Loop) as usize + 1;
for &out in node.outputs.iter() {
if fuc.nodes[out].is_data_phi()
&& atr(out) != atr(fuc.nodes[out].inputs[index])
{
self.emit(instrs::cp(
atr(out),
atr(fuc.nodes[out].inputs[index]),
));
}
}
let rel = Reloc::new(self.code.len(), 1, 4);
self.jump_relocs.push((nid, rel));
self.emit(instrs::jmp(0));
} else {
let index = (node.kind != Kind::Loop) as usize + 1;
for &out in node.outputs.iter() {
if fuc.nodes[out].is_data_phi()
&& atr(out) != atr(fuc.nodes[out].inputs[index])
{
self.emit(instrs::cp(
atr(out),
atr(fuc.nodes[out].inputs[index]),
));
}
}
}
}
Kind::Return => {
let &[_, mut ret, ..] = node.inputs.as_slice() else { unreachable!() };
match retl {
None => {}
Some(PLoc::Reg(r, _)) if sig.ret.loc(tys) == Loc::Reg => {
self.emit(instrs::cp(r, atr(ret)));
}
Some(PLoc::Reg(r, size)) | Some(PLoc::WideReg(r, size)) => {
ret = match fuc.nodes[ret].kind {
Kind::Load { .. } => fuc.nodes[ret].inputs[1],
_ => ret,
};
self.emit(instrs::ld(r, atr(ret), 0, size))
}
Some(PLoc::Ref(_, size)) => {
ret = match fuc.nodes[ret].kind {
Kind::Load { .. } => fuc.nodes[ret].inputs[1],
_ => ret,
};
let [src, dst] = [atr(ret), atr(MEM)];
if let Ok(size) = u16::try_from(size) {
self.emit(instrs::bmc(src, dst, size));
} else {
for _ in 0..size / u16::MAX as u32 {
self.emit(instrs::bmc(src, dst, u16::MAX));
self.emit(instrs::addi64(src, src, u16::MAX as _));
self.emit(instrs::addi64(dst, dst, u16::MAX as _));
}
self.emit(instrs::bmc(src, dst, size as u16));
self.emit(instrs::addi64(src, src, size.wrapping_neg() as _));
self.emit(instrs::addi64(dst, dst, size.wrapping_neg() as _));
}
}
}
if i != fuc.func.blocks.len() - 1 {
let rel = Reloc::new(self.code.len(), 1, 4);
self.ret_relocs.push(rel);
self.emit(instrs::jmp(0));
}
}
Kind::Die => self.emit(instrs::un()),
Kind::CInt { value } if node.ty.is_float() => {
self.emit(match node.ty {
ty::Id::F32 => instrs::li32(
atr(nid),
(f64::from_bits(value as _) as f32).to_bits(),
),
ty::Id::F64 => instrs::li64(atr(nid), value as _),
_ => unreachable!(),
});
}
Kind::CInt { value } => self.emit(match tys.size_of(node.ty) {
1 => instrs::li8(atr(nid), value as _),
2 => instrs::li16(atr(nid), value as _),
4 => instrs::li32(atr(nid), value as _),
_ => instrs::li64(atr(nid), value as _),
}),
Kind::UnOp { op } => {
let op = op
.unop(node.ty, fuc.nodes[node.inputs[1]].ty)
.expect("TODO: unary operator not supported");
self.emit(op(atr(nid), atr(node.inputs[1])));
}
Kind::BinOp { .. } if node.lock_rc != 0 => {}
Kind::BinOp { op } => {
let &[.., lhs, rhs] = node.inputs.as_slice() else { unreachable!() };
if let Kind::CInt { value } = fuc.nodes[rhs].kind
&& fuc.nodes[rhs].lock_rc != 0
&& let Some(op) = op.imm_binop(node.ty)
{
self.emit(op(atr(nid), atr(lhs), value as _));
} else if let Some(op) =
op.binop(node.ty).or(op.float_cmp(fuc.nodes[lhs].ty))
{
self.emit(op(atr(nid), atr(lhs), atr(rhs)));
} else if let Some(against) = op.cmp_against() {
let op_ty = fuc.nodes[lhs].ty;
self.emit(extend(fuc.nodes[lhs].ty, fuc.nodes[lhs].ty.extend(), 0, 0));
self.emit(extend(fuc.nodes[rhs].ty, fuc.nodes[rhs].ty.extend(), 1, 1));
if op_ty.is_float() && matches!(op, TokenKind::Le | TokenKind::Ge) {
let opop = match op {
TokenKind::Le => TokenKind::Gt,
TokenKind::Ge => TokenKind::Lt,
_ => unreachable!(),
};
let op_fn = opop.float_cmp(op_ty).unwrap();
self.emit(op_fn(atr(nid), atr(lhs), atr(rhs)));
self.emit(instrs::not(atr(nid), atr(nid)));
} else if op_ty.is_integer() {
let op_fn =
if op_ty.is_signed() { instrs::cmps } else { instrs::cmpu };
self.emit(op_fn(atr(nid), atr(lhs), atr(rhs)));
self.emit(instrs::cmpui(atr(nid), atr(nid), against));
if matches!(op, TokenKind::Eq | TokenKind::Lt | TokenKind::Gt) {
self.emit(instrs::not(atr(nid), atr(nid)));
}
} else {
todo!("unhandled operator: {op}");
}
} else {
todo!("unhandled operator: {op}");
}
}
Kind::Call { args, func } => {
let (ret, mut parama) = tys.parama(node.ty);
let mut args = args.args();
let mut allocs = node.inputs[1..].iter();
while let Some(arg) = args.next(tys) {
let Arg::Value(ty) = arg else { continue };
let Some(loc) = parama.next(ty, tys) else { continue };
let mut arg = *allocs.next().unwrap();
let (rg, size) = match loc {
PLoc::Reg(rg, size) if ty.loc(tys) == Loc::Stack => (rg, size),
PLoc::WideReg(rg, size) => (rg, size),
PLoc::Ref(r, ..) => {
arg = match fuc.nodes[arg].kind {
Kind::Load { .. } => fuc.nodes[arg].inputs[1],
_ => arg,
};
self.emit(instrs::cp(r, atr(arg)));
continue;
}
PLoc::Reg(r, ..) => {
self.emit(instrs::cp(r, atr(arg)));
continue;
}
};
arg = match fuc.nodes[arg].kind {
Kind::Load { .. } => fuc.nodes[arg].inputs[1],
_ => arg,
};
self.emit(instrs::ld(rg, atr(arg), 0, size));
}
debug_assert!(
!matches!(ret, Some(PLoc::Ref(..))) || allocs.next().is_some()
);
if func == ty::ECA {
self.emit(instrs::eca());
} else {
self.relocs.push(TypedReloc {
target: ty::Kind::Func(func).compress(),
reloc: Reloc::new(self.code.len(), 3, 4),
});
self.emit(instrs::jal(reg::RET_ADDR, reg::ZERO, 0));
}
match ret {
Some(PLoc::WideReg(r, size)) => {
debug_assert_eq!(
fuc.nodes[*node.inputs.last().unwrap()].kind,
Kind::Stck
);
let stck = self.offsets[*node.inputs.last().unwrap() as usize];
self.emit(instrs::st(r, reg::STACK_PTR, stck as _, size));
}
Some(PLoc::Reg(r, size)) if node.ty.loc(tys) == Loc::Stack => {
debug_assert_eq!(
fuc.nodes[*node.inputs.last().unwrap()].kind,
Kind::Stck
);
let stck = self.offsets[*node.inputs.last().unwrap() as usize];
self.emit(instrs::st(r, reg::STACK_PTR, stck as _, size));
}
Some(PLoc::Reg(r, ..)) => self.emit(instrs::cp(atr(nid), r)),
None | Some(PLoc::Ref(..)) => {}
}
}
Kind::Global { global } => {
let reloc = Reloc::new(self.code.len(), 3, 4);
self.relocs.push(TypedReloc {
target: ty::Kind::Global(global).compress(),
reloc,
});
self.emit(instrs::lra(atr(nid), 0, 0));
}
Kind::Stck => {
let base = reg::STACK_PTR;
let offset = self.offsets[nid as usize];
self.emit(instrs::addi64(atr(nid), base, offset as _));
}
Kind::Load => {
let mut region = node.inputs[1];
let mut offset = 0;
if fuc.nodes[region].kind == (Kind::BinOp { op: TokenKind::Add })
&& let Kind::CInt { value } =
fuc.nodes[fuc.nodes[region].inputs[2]].kind
{
region = fuc.nodes[region].inputs[1];
offset = value as Offset;
}
let size = tys.size_of(node.ty);
if node.ty.loc(tys) != Loc::Stack {
let (base, offset) = match fuc.nodes[region].kind {
Kind::Stck => {
(reg::STACK_PTR, self.offsets[region as usize] + offset)
}
_ => (atr(region), offset),
};
self.emit(instrs::ld(atr(nid), base, offset as _, size as _));
}
}
Kind::Stre if node.inputs[1] == VOID => {}
Kind::Stre => {
let mut region = node.inputs[2];
let mut offset = 0;
let size = u16::try_from(tys.size_of(node.ty)).expect("TODO");
if fuc.nodes[region].kind == (Kind::BinOp { op: TokenKind::Add })
&& let Kind::CInt { value } =
fuc.nodes[fuc.nodes[region].inputs[2]].kind
&& node.ty.loc(tys) == Loc::Reg
{
region = fuc.nodes[region].inputs[1];
offset = value as Offset;
}
let nd = &fuc.nodes[region];
let value = node.inputs[1];
let (base, offset, src) = match nd.kind {
Kind::Stck if node.ty.loc(tys) == Loc::Reg => {
(reg::STACK_PTR, self.offsets[region as usize] + offset, value)
}
_ => (atr(region), offset, match fuc.nodes[value].kind {
Kind::Load { .. } => fuc.nodes[value].inputs[1],
_ => value,
}),
};
match node.ty.loc(tys) {
Loc::Reg => self.emit(instrs::st(atr(src), base, offset as _, size)),
Loc::Stack => {
debug_assert_eq!(offset, 0);
self.emit(instrs::bmc(atr(src), base, size))
}
}
}
Kind::Mem => self.emit(instrs::cp(atr(MEM), reg::RET)),
Kind::Arg => {}
e @ (Kind::Start
| Kind::Entry
| Kind::End
| Kind::Loops
| Kind::Then
| Kind::Else
| Kind::Phi
| Kind::Assert { .. }) => unreachable!("{e:?}"),
}
}
}
self.ralloc_my = res;
let bundle_count = self.ralloc_my.bundles.len() + (reg_offset as usize);
(
if fuc.tail {
bundle_count.saturating_sub(reg::RET_ADDR as _)
} else {
assert!(bundle_count < reg::STACK_PTR as usize, "TODO: spill memory");
self.ralloc_my.bundles.len()
},
fuc.tail,
)
}
}
pub struct Function<'a> {
sig: Sig,
tail: bool,
backrefs: Vec<u16>,
nodes: &'a mut Nodes,
tys: &'a Types,
visited: BitSet,
func: Func,
}
impl Function<'_> {
fn vreg_count(&self) -> usize {
self.nodes.values.len()
}
fn uses_of(&self, nid: Nid, buf: &mut Vec<Nid>) {
if self.nodes[nid].kind.is_cfg() && !matches!(self.nodes[nid].kind, Kind::Call { .. }) {
return;
}
self.nodes[nid]
.outputs
.iter()
.filter(|&&n| self.nodes.is_data_dep(nid, n))
.collect_into(buf);
}
fn phi_inputs_of(&self, nid: Nid, buf: &mut Vec<Nid>) {
match self.nodes[nid].kind {
Kind::Region => {
for &inp in self.nodes[nid].outputs.as_slice() {
if self.nodes[inp].is_data_phi() {
buf.extend(&self.nodes[inp].inputs[1..]);
buf.push(inp);
}
}
}
Kind::Loop => {
for &inp in self.nodes[nid].outputs.as_slice() {
if self.nodes[inp].is_data_phi() {
buf.push(self.nodes[inp].inputs[1]);
buf.push(inp);
buf.push(self.nodes[inp].inputs[2]);
}
}
}
_ => {}
}
}
fn instr_of(&self, nid: Nid) -> Option<Nid> {
if self.nodes[nid].kind == Kind::Phi || self.nodes[nid].lock_rc != 0 {
return None;
}
debug_assert_ne!(self.backrefs[nid as usize], Nid::MAX, "{:?}", self.nodes[nid]);
Some(self.backrefs[nid as usize])
}
fn block_of(&self, nid: Nid) -> Nid {
debug_assert!(self.nodes[nid].kind.starts_basic_block());
self.backrefs[nid as usize]
}
fn idom_of(&self, mut nid: Nid) -> Nid {
while !self.nodes[nid].kind.starts_basic_block() {
nid = self.nodes.idom(nid);
}
nid
}
fn use_block(&self, inst: Nid, uinst: Nid) -> Nid {
let mut block = self.nodes.use_block(inst, uinst);
while !self.nodes[block].kind.starts_basic_block() {
block = self.nodes.idom(block);
}
block
}
}
impl core::fmt::Debug for Function<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
for block in &self.func.blocks {
writeln!(f, "{:?}", self.nodes[block.entry].kind)?;
for &instr in &self.func.instrs[block.range.clone()] {
writeln!(f, "{:?}", self.nodes[instr].kind)?;
}
}
Ok(())
}
}
impl<'a> Function<'a> {
fn new(nodes: &'a mut Nodes, tys: &'a Types, sig: Sig) -> Self {
let mut s = Self {
backrefs: vec![u16::MAX; nodes.values.len()],
tail: true,
nodes,
tys,
sig,
visited: Default::default(),
func: Default::default(),
};
s.visited.clear(s.nodes.values.len());
s.emit_node(VOID);
s
}
fn add_block(&mut self, entry: Nid) {
self.func
.blocks
.push(Block { range: self.func.instrs.len()..self.func.instrs.len(), entry });
self.backrefs[entry as usize] = self.func.blocks.len() as u16 - 1;
}
fn close_block(&mut self, exit: Nid) {
if !matches!(self.nodes[exit].kind, Kind::Loop | Kind::Region) {
self.add_instr(exit);
} else {
self.func.instrs.push(exit);
}
let prev = self.func.blocks.last_mut().unwrap();
prev.range.end = self.func.instrs.len();
}
fn add_instr(&mut self, nid: Nid) {
debug_assert_ne!(self.nodes[nid].kind, Kind::Loop);
self.backrefs[nid as usize] = self.func.instrs.len() as u16;
self.func.instrs.push(nid);
}
fn emit_node(&mut self, nid: Nid) {
if matches!(self.nodes[nid].kind, Kind::Region | Kind::Loop) {
match (self.nodes[nid].kind, self.visited.set(nid)) {
(Kind::Loop, false) | (Kind::Region, true) => {
self.close_block(nid);
return;
}
_ => {}
}
} else if !self.visited.set(nid) {
return;
}
if self.nodes.is_never_used(nid, self.tys) {
self.nodes.lock(nid);
return;
}
let mut node = self.nodes[nid].clone();
match node.kind {
Kind::Start => {
debug_assert_matches!(self.nodes[node.outputs[0]].kind, Kind::Entry);
self.add_block(VOID);
self.emit_node(node.outputs[0])
}
Kind::If => {
let &[_, cond] = node.inputs.as_slice() else { unreachable!() };
let &[mut then, mut else_] = node.outputs.as_slice() else { unreachable!() };
if let Kind::BinOp { op } = self.nodes[cond].kind
&& let Some((_, swapped)) = op.cond_op(node.ty)
&& swapped
{
mem::swap(&mut then, &mut else_);
}
self.close_block(nid);
self.emit_node(then);
self.emit_node(else_);
}
Kind::Region | Kind::Loop => {
self.close_block(nid);
self.add_block(nid);
self.reschedule_block(nid, &mut node.outputs);
for o in node.outputs.into_iter().rev() {
self.emit_node(o);
}
}
Kind::Return | Kind::Die => {
self.close_block(nid);
self.emit_node(node.outputs[0]);
}
Kind::Entry => {
let (ret, mut parama) = self.tys.parama(self.sig.ret);
let mut typs = self.sig.args.args();
#[expect(clippy::unnecessary_to_owned)]
let mut args = self.nodes[VOID].outputs[ARG_START..].to_owned().into_iter();
while let Some(ty) = typs.next_value(self.tys) {
let arg = args.next().unwrap();
debug_assert_eq!(self.nodes[arg].kind, Kind::Arg);
match parama.next(ty, self.tys) {
None => {}
Some(_) => self.add_instr(arg),
}
}
if let Some(PLoc::Ref(..)) = ret {
self.add_instr(MEM);
}
self.reschedule_block(nid, &mut node.outputs);
for o in node.outputs.into_iter().rev() {
self.emit_node(o);
}
}
Kind::Then | Kind::Else => {
self.add_block(nid);
self.reschedule_block(nid, &mut node.outputs);
for o in node.outputs.into_iter().rev() {
self.emit_node(o);
}
}
Kind::Call { func, .. } => {
self.tail &= func == ty::ECA;
self.add_instr(nid);
self.reschedule_block(nid, &mut node.outputs);
for o in node.outputs.into_iter().rev() {
if self.nodes[o].inputs[0] == nid
|| (matches!(self.nodes[o].kind, Kind::Loop | Kind::Region)
&& self.nodes[o].inputs[1] == nid)
{
self.emit_node(o);
}
}
}
Kind::CInt { .. }
| Kind::BinOp { .. }
| Kind::UnOp { .. }
| Kind::Global { .. }
| Kind::Load { .. }
| Kind::Stre
| Kind::Stck => self.add_instr(nid),
Kind::End | Kind::Phi | Kind::Arg | Kind::Mem | Kind::Loops => {}
Kind::Assert { .. } => unreachable!(),
}
}
fn reschedule_block(&mut self, from: Nid, outputs: &mut Vc) {
let from = Some(&from);
let mut buf = Vec::with_capacity(outputs.len());
let mut seen = BitSet::default();
seen.clear(self.nodes.values.len());
for &o in outputs.iter() {
if !self.nodes.is_cfg(o) {
continue;
}
seen.set(o);
let mut cursor = buf.len();
buf.push(o);
while let Some(&n) = buf.get(cursor) {
for &i in &self.nodes[n].inputs[1..] {
if from == self.nodes[i].inputs.first()
&& self.nodes[i]
.outputs
.iter()
.all(|&o| self.nodes[o].inputs.first() != from || seen.get(o))
&& seen.set(i)
{
buf.push(i);
}
}
cursor += 1;
}
}
for &o in outputs.iter() {
if !seen.set(o) {
continue;
}
let mut cursor = buf.len();
buf.push(o);
while let Some(&n) = buf.get(cursor) {
for &i in &self.nodes[n].inputs[1..] {
if from == self.nodes[i].inputs.first()
&& self.nodes[i]
.outputs
.iter()
.all(|&o| self.nodes[o].inputs.first() != from || seen.get(o))
&& seen.set(i)
{
buf.push(i);
}
}
cursor += 1;
}
}
debug_assert!(
outputs.len() == buf.len() || outputs.len() == buf.len() + 1,
"{:?} {:?}",
outputs,
buf
);
if buf.len() + 1 == outputs.len() {
outputs.remove(outputs.len() - 1);
}
outputs.copy_from_slice(&buf);
}
}
pub struct Env<'a> {
ctx: &'a Function<'a>,
func: &'a Func,
res: &'a mut Res,
}
impl<'a> Env<'a> {
pub fn new(ctx: &'a Function<'a>, func: &'a Func, res: &'a mut Res) -> Self {
Self { ctx, func, res }
}
pub fn run(&mut self) {
self.res.bundles.clear();
self.res.node_to_reg.clear();
self.res.node_to_reg.resize(self.ctx.vreg_count(), 0);
debug_assert!(self.res.dfs_buf.is_empty());
debug_assert!(self.res.use_buf.is_empty());
debug_assert!(self.res.phi_input_buf.is_empty());
let mut bundle = Bundle::new(self.func.instrs.len());
let mut visited = BitSet::with_capacity(self.ctx.nodes.values.len());
let mut use_buf = mem::take(&mut self.res.use_buf);
let mut phi_input_buf = mem::take(&mut self.res.phi_input_buf);
for block in &self.func.blocks {
self.ctx.phi_inputs_of(block.entry, &mut phi_input_buf);
for param in phi_input_buf.drain(..) {
if !visited.set(param) {
continue;
}
self.append_bundle(param, &mut bundle, &mut use_buf);
}
}
self.res.phi_input_buf = phi_input_buf;
for &inst in &self.func.instrs {
if visited.get(inst) || inst == 0 {
continue;
}
self.append_bundle(inst, &mut bundle, &mut use_buf);
}
self.res.use_buf = use_buf;
}
fn append_bundle(&mut self, inst: Nid, bundle: &mut Bundle, use_buf: &mut Vec<Nid>) {
let mut dom = self.ctx.idom_of(inst);
if self.ctx.nodes[dom].kind == Kind::Loop && self.ctx.nodes[inst].kind == Kind::Phi {
dom = self.ctx.nodes.idom(dom);
dom = self.ctx.idom_of(dom);
}
self.ctx.uses_of(inst, use_buf);
for uinst in use_buf.drain(..) {
let cursor = self.ctx.use_block(inst, uinst);
self.reverse_cfg_dfs(cursor, dom, |_, n, b| {
let mut range = b.range.clone();
range.start =
range.start.max(self.ctx.instr_of(inst).map_or(0, |n| n + 1) as usize);
range.end = range.end.min(
self.ctx
.instr_of(uinst)
.filter(|_| self.ctx.nodes.loop_depth(dom) == self.ctx.nodes.loop_depth(n))
.map_or(Nid::MAX, |n| n + 1) as usize,
);
bundle.add(range);
});
}
match self.res.bundles.iter_mut().enumerate().find(|(_, b)| !b.overlaps(bundle)) {
Some((i, other)) => {
other.merge(bundle);
bundle.clear();
self.res.node_to_reg[inst as usize] = i as Reg + 1;
}
None => {
self.res.bundles.push(mem::replace(bundle, Bundle::new(self.func.instrs.len())));
self.res.node_to_reg[inst as usize] = self.res.bundles.len() as Reg;
}
}
}
fn reverse_cfg_dfs(
&mut self,
from: Nid,
until: Nid,
mut each: impl FnMut(&mut Self, Nid, &Block),
) {
debug_assert!(self.res.dfs_buf.is_empty());
self.res.dfs_buf.push(from);
self.res.dfs_seem.clear(self.ctx.nodes.values.len());
while let Some(nid) = self.res.dfs_buf.pop() {
each(self, nid, &self.func.blocks[self.ctx.block_of(nid) as usize]);
if nid == until {
continue;
}
match self.ctx.nodes[nid].kind {
Kind::Then | Kind::Else | Kind::Region | Kind::Loop => {
for &n in self.ctx.nodes[nid].inputs.iter() {
let d = self.ctx.idom_of(n);
if self.res.dfs_seem.set(d) {
self.res.dfs_buf.push(d);
}
}
}
Kind::Start => {}
_ => unreachable!(),
}
}
}
}
#[derive(Default)]
pub struct Res {
pub bundles: Vec<Bundle>,
pub node_to_reg: Vec<Reg>,
use_buf: Vec<Nid>,
phi_input_buf: Vec<Nid>,
dfs_buf: Vec<Nid>,
dfs_seem: BitSet,
}
pub struct Bundle {
taken: Vec<bool>,
}
impl Bundle {
fn new(size: usize) -> Self {
Self { taken: vec![false; size] }
}
fn add(&mut self, range: Range<usize>) {
self.taken[range].fill(true);
}
fn overlaps(&self, other: &Self) -> bool {
self.taken.iter().zip(other.taken.iter()).any(|(a, b)| a & b)
}
fn merge(&mut self, other: &Self) {
debug_assert!(!self.overlaps(other));
self.taken.iter_mut().zip(other.taken.iter()).for_each(|(a, b)| *a |= *b);
}
fn clear(&mut self) {
self.taken.fill(false);
}
}
#[derive(Default)]
pub struct Func {
pub blocks: Vec<Block>,
pub instrs: Vec<Nid>,
}
pub struct Block {
pub range: Range<usize>,
pub entry: Nid,
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,40 +1,20 @@
#![expect(dead_code)]
use {
alloc::alloc,
core::{
alloc::Layout,
fmt::Debug,
hint::unreachable_unchecked,
marker::PhantomData,
mem::MaybeUninit,
ops::{Deref, DerefMut, Not, Range},
ops::{Deref, DerefMut, Not},
ptr::Unique,
},
};
fn decide(b: bool, name: &'static str) -> Result<(), &'static str> {
b.then_some(()).ok_or(name)
}
pub fn is_snake_case(str: &str) -> Result<(), &'static str> {
decide(str.bytes().all(|c| matches!(c, b'a'..=b'z' | b'0'..=b'9' | b'_')), "snake_case")
}
pub fn is_pascal_case(str: &str) -> Result<(), &'static str> {
decide(
str.as_bytes()[0].is_ascii_uppercase() && str.bytes().all(|c| c.is_ascii_alphanumeric()),
"PascalCase",
)
}
pub fn is_screaming_case(str: &str) -> Result<(), &'static str> {
decide(str.bytes().all(|c| matches!(c, b'A'..=b'Z' | b'0'..=b'9' | b'_')), "SCREAMING_CASE")
}
type Nid = u16;
type BitSetUnit = usize;
pub union BitSet {
inline: BitSetUnit,
inline: usize,
alloced: Unique<AllocedBitSet>,
}
@ -78,9 +58,9 @@ impl Default for BitSet {
}
impl BitSet {
const FLAG: BitSetUnit = 1 << (Self::UNIT - 1);
const FLAG: usize = 1 << (Self::UNIT - 1);
const INLINE_ELEMS: usize = Self::UNIT - 1;
pub const UNIT: usize = core::mem::size_of::<BitSetUnit>() * 8;
const UNIT: usize = core::mem::size_of::<usize>() * 8;
pub fn with_capacity(len: usize) -> Self {
let mut s = Self::default();
@ -92,7 +72,7 @@ impl BitSet {
unsafe { self.inline & Self::FLAG != 0 }
}
fn data_and_len(&self) -> (&[BitSetUnit], usize) {
fn data_and_len(&self) -> (&[usize], usize) {
unsafe {
if self.is_inline() {
(core::slice::from_ref(&self.inline), Self::INLINE_ELEMS)
@ -100,16 +80,16 @@ impl BitSet {
let small_vec = self.alloced.as_ref();
(
core::slice::from_raw_parts(
&small_vec.data as *const _ as *const BitSetUnit,
&small_vec.data as *const _ as *const usize,
small_vec.cap,
),
small_vec.cap * Self::UNIT,
small_vec.cap * core::mem::size_of::<usize>() * 8,
)
}
}
}
fn data_mut_and_len(&mut self) -> (&mut [BitSetUnit], usize) {
fn data_mut_and_len(&mut self) -> (&mut [usize], usize) {
unsafe {
if self.is_inline() {
(core::slice::from_mut(&mut self.inline), INLINE_ELEMS)
@ -117,7 +97,7 @@ impl BitSet {
let small_vec = self.alloced.as_mut();
(
core::slice::from_raw_parts_mut(
&mut small_vec.data as *mut _ as *mut BitSetUnit,
&mut small_vec.data as *mut _ as *mut usize,
small_vec.cap,
),
small_vec.cap * Self::UNIT,
@ -144,12 +124,11 @@ impl BitSet {
let index = index as usize;
let (mut data, len) = self.data_mut_and_len();
if core::intrinsics::unlikely(index >= len) {
self.grow((index + 1).next_power_of_two().max(4 * Self::UNIT));
self.grow(index.next_power_of_two().max(4 * Self::UNIT));
(data, _) = self.data_mut_and_len();
}
let (elem, bit) = Self::indexes(index);
debug_assert!(elem < data.len(), "{} < {}", elem, data.len());
let elem = unsafe { data.get_unchecked_mut(elem) };
let prev = *elem;
*elem |= 1 << bit;
@ -163,7 +142,7 @@ impl BitSet {
let (ptr, prev_len) = unsafe {
if self.is_inline() {
let ptr = alloc::alloc(layout);
*ptr.add(off).cast::<BitSetUnit>() = self.inline & !Self::FLAG;
*ptr.add(off).cast::<usize>() = self.inline & !Self::FLAG;
(ptr, 1)
} else {
let prev_len = self.alloced.as_ref().cap;
@ -174,7 +153,7 @@ impl BitSet {
unsafe {
MaybeUninit::fill(
core::slice::from_raw_parts_mut(
ptr.add(off).cast::<MaybeUninit<BitSetUnit>>().add(prev_len),
ptr.add(off).cast::<MaybeUninit<usize>>().add(prev_len),
slot_count - prev_len,
),
0,
@ -187,7 +166,7 @@ impl BitSet {
fn layout(slot_count: usize) -> (core::alloc::Layout, usize) {
unsafe {
core::alloc::Layout::new::<AllocedBitSet>()
.extend(Layout::array::<BitSetUnit>(slot_count).unwrap_unchecked())
.extend(Layout::array::<usize>(slot_count).unwrap_unchecked())
.unwrap_unchecked()
}
}
@ -205,10 +184,6 @@ impl BitSet {
pub fn clear(&mut self, len: usize) {
self.reserve(len);
self.clear_as_is();
}
pub fn clear_as_is(&mut self) {
if self.is_inline() {
unsafe { self.inline &= Self::FLAG };
} else {
@ -216,11 +191,7 @@ impl BitSet {
}
}
pub fn approx_unit_cap(&self) -> usize {
self.data_and_len().0.len()
}
pub fn units<'a>(&'a self, slot: &'a mut BitSetUnit) -> &'a [BitSetUnit] {
pub fn units<'a>(&'a self, slot: &'a mut usize) -> &'a [usize] {
if self.is_inline() {
*slot = unsafe { self.inline } & !Self::FLAG;
core::slice::from_ref(slot)
@ -229,47 +200,36 @@ impl BitSet {
}
}
pub fn units_mut(&mut self) -> Option<&mut [BitSetUnit]> {
self.is_inline().not().then(|| self.data_mut_and_len().0)
}
pub fn reserve(&mut self, len: usize) {
if len > self.data_and_len().1 {
self.grow(len.next_power_of_two().max(4 * Self::UNIT));
}
}
pub fn set_range(&mut self, proj_range: Range<usize>) {
if proj_range.is_empty() {
return;
}
self.reserve(proj_range.end);
let (units, _) = self.data_mut_and_len();
if proj_range.start / Self::UNIT == (proj_range.end - 1) / Self::UNIT {
debug_assert!(proj_range.len() <= Self::UNIT);
let mask = ((1 << proj_range.len()) - 1) << (proj_range.start % Self::UNIT);
units[proj_range.start / Self::UNIT] |= mask;
pub fn units_mut(&mut self) -> Result<&mut [usize], &mut InlineBitSetView> {
if self.is_inline() {
Err(unsafe {
core::mem::transmute::<&mut usize, &mut InlineBitSetView>(&mut self.inline)
})
} else {
let fill_range = proj_range.start.div_ceil(Self::UNIT)..proj_range.end / Self::UNIT;
units[fill_range].fill(BitSetUnit::MAX);
let prefix_len = Self::UNIT - proj_range.start % Self::UNIT;
let prefix_mask = ((1 << prefix_len) - 1) << (proj_range.start % Self::UNIT);
units[proj_range.start / Self::UNIT] |= prefix_mask;
let postfix_len = proj_range.end % Self::UNIT;
let postfix_mask = (1 << postfix_len) - 1;
units[proj_range.end / Self::UNIT] |= postfix_mask;
Ok(self.data_mut_and_len().0)
}
}
}
pub struct InlineBitSetView(usize);
impl InlineBitSetView {
pub(crate) fn add_mask(&mut self, tmp: usize) {
debug_assert!(tmp & BitSet::FLAG == 0);
self.0 |= tmp;
}
}
pub struct BitSetIter<'a> {
index: usize,
current: BitSetUnit,
remining: &'a [BitSetUnit],
current: usize,
remining: &'a [usize],
}
impl Iterator for BitSetIter<'_> {
@ -289,7 +249,7 @@ impl Iterator for BitSetIter<'_> {
struct AllocedBitSet {
cap: usize,
data: [BitSetUnit; 0],
data: [usize; 0],
}
#[cfg(test)]
@ -400,26 +360,25 @@ impl Vc {
}
pub fn push(&mut self, value: Nid) {
if let Some(layout) = self.layout() {
if unsafe { self.alloced.len == self.alloced.cap } {
unsafe {
self.alloced.cap *= 2;
self.alloced.base = Unique::new_unchecked(
alloc::realloc(
self.alloced.base.as_ptr().cast(),
layout,
self.alloced.cap as usize * core::mem::size_of::<Nid>(),
)
.cast(),
);
}
if let Some(layout) = self.layout()
&& unsafe { self.alloced.len == self.alloced.cap }
{
unsafe {
self.alloced.cap *= 2;
self.alloced.base = Unique::new_unchecked(
alloc::realloc(
self.alloced.base.as_ptr().cast(),
layout,
self.alloced.cap as usize * core::mem::size_of::<Nid>(),
)
.cast(),
);
}
} else if self.len() == INLINE_ELEMS {
unsafe {
let mut allcd =
Self::alloc((self.inline.cap + 1).next_power_of_two() as _, self.len());
core::ptr::copy_nonoverlapping(self.as_ptr(), allcd.as_mut_ptr(), self.len());
debug_assert!(!allcd.is_inline());
*self = allcd;
}
}
@ -573,117 +532,3 @@ struct AllocedVc {
len: Nid,
base: Unique<Nid>,
}
pub trait Ent: Copy {
fn new(index: usize) -> Self;
fn index(self) -> usize;
}
#[repr(transparent)]
pub struct EntSlice<K: Ent, T> {
k: PhantomData<fn(K)>,
data: [T],
}
impl<'a, K: Ent, T> From<&'a [T]> for &'a EntSlice<K, T> {
fn from(value: &'a [T]) -> Self {
unsafe { core::mem::transmute(value) }
}
}
impl<K: Ent, T> core::ops::Index<K> for EntSlice<K, T> {
type Output = T;
fn index(&self, index: K) -> &Self::Output {
&self.data[index.index()]
}
}
pub struct EntVec<K: Ent, T> {
data: ::alloc::vec::Vec<T>,
k: PhantomData<fn(K)>,
}
impl<K: Ent, T> Default for EntVec<K, T> {
fn default() -> Self {
Self { data: Default::default(), k: PhantomData }
}
}
impl<K: Ent, T> EntVec<K, T> {
pub fn clear(&mut self) {
self.data.clear();
}
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
pub fn len(&self) -> usize {
self.data.len()
}
pub fn push(&mut self, value: T) -> K {
let k = K::new(self.data.len());
self.data.push(value);
k
}
pub fn next(&self, index: K) -> Option<&T> {
self.data.get(index.index() + 1)
}
pub fn shadow(&mut self, len: usize)
where
T: Default,
{
if self.data.len() < len {
self.data.resize_with(len, Default::default);
}
}
pub fn iter(&self) -> core::slice::Iter<T> {
self.data.iter()
}
}
impl<K: Ent, T> core::ops::Index<K> for EntVec<K, T> {
type Output = T;
fn index(&self, index: K) -> &Self::Output {
&self.data[index.index()]
}
}
impl<K: Ent, T> core::ops::IndexMut<K> for EntVec<K, T> {
fn index_mut(&mut self, index: K) -> &mut Self::Output {
&mut self.data[index.index()]
}
}
macro_rules! decl_ent {
($(
$vis:vis struct $name:ident($index:ty);
)*) => {$(
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
$vis struct $name($index);
impl crate::utils::Ent for $name {
fn new(index: usize) -> Self {
Self(index as $index)
}
fn index(self) -> usize {
self.0 as _
}
}
impl core::fmt::Display for $name {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, concat!(stringify!($name), "{}"), self.0)
}
}
)*};
}
pub(crate) use decl_ent;

View file

@ -4,44 +4,41 @@ main:
LI32 r32, 1148846080w
CP r2, r32
JAL r31, r0, :sin
CP r33, r1
FMUL32 r32, r33, r32
FTI32 r32, r32, 1b
CP r1, r32
FMUL32 r33, r1, r32
FTI32 r1, r33, 1b
LD r31, r254, 0a, 24h
ADDI64 r254, r254, 24d
JALA r0, r31, 0a
sin:
CP r13, r2
LI32 r14, 1124073472w
LI32 r15, 1078530011w
FMUL32 r14, r13, r14
FDIV32 r14, r14, r15
FTI32 r14, r14, 1b
ANDI r15, r14, 255d
MULI64 r15, r15, 4d
LRA r16, r0, :sin_table
LI32 r17, 1086918619w
ITF32 r18, r14
ADDI64 r14, r14, 64d
ADD64 r15, r16, r15
LI32 r19, 1132462080w
FMUL32 r17, r18, r17
ANDI r14, r14, 255d
LI32 r18, 1056964608w
LD r15, r15, 0a, 4h
FDIV32 r17, r17, r19
MULI64 r14, r14, 4d
FMUL32 r18, r15, r18
FSUB32 r13, r13, r17
ADD64 r14, r16, r14
FMUL32 r16, r13, r18
LD r14, r14, 0a, 4h
FSUB32 r14, r14, r16
FMUL32 r13, r14, r13
FADD32 r13, r15, r13
CP r1, r13
LI32 r4, 1124073472w
LI32 r5, 1078530011w
FMUL32 r7, r2, r4
FDIV32 r9, r7, r5
FTI32 r11, r9, 1b
ANDI r10, r11, 255d
ITF64 r5, r11
MULI64 r4, r10, 4d
LRA r3, r0, :SIN_TABLE
LI32 r7, 1086918619w
FC64T32 r9, r5, 1b
ADDI64 r5, r11, 64d
ADD64 r8, r3, r4
LI32 r1, 1132462080w
FMUL32 r6, r9, r7
ANDI r7, r5, 255d
LI32 r5, 1056964608w
LD r4, r8, 0a, 4h
FDIV32 r8, r6, r1
MULI64 r6, r7, 4d
FMUL32 r10, r4, r5
FSUB32 r11, r2, r8
ADD64 r9, r3, r6
FMUL32 r2, r11, r10
LD r12, r9, 0a, 4h
FSUB32 r5, r12, r2
FMUL32 r7, r5, r11
FADD32 r1, r4, r7
JALA r0, r31, 0a
code size: 1311
code size: 1303
ret: 826
status: Ok(())

View file

@ -1,6 +1,6 @@
main:
CP r1, r0
LI64 r1, 0d
JALA r0, r31, 0a
code size: 22
code size: 29
ret: 0
status: Ok(())

View file

@ -1,6 +1,6 @@
main:
CP r1, r0
LI64 r1, 0d
JALA r0, r31, 0a
code size: 22
code size: 29
ret: 0
status: Ok(())

View file

@ -2,31 +2,26 @@ main:
ADDI64 r254, r254, -56d
ST r31, r254, 24a, 32h
LI64 r32, 1d
ADDI64 r33, r254, 0d
ADDI64 r2, r254, 0d
ST r32, r254, 0a, 8h
LI64 r34, 2d
ST r34, r254, 8a, 8h
LI64 r33, 2d
ST r33, r254, 8a, 8h
LI64 r34, 4d
ST r34, r254, 16a, 8h
CP r2, r33
JAL r31, r0, :pass
CP r33, r1
ADD64 r32, r33, r32
CP r1, r32
ADD64 r1, r1, r32
LD r31, r254, 24a, 32h
ADDI64 r254, r254, 56d
JALA r0, r31, 0a
pass:
CP r13, r2
LD r14, r13, 8a, 8h
MULI64 r15, r14, 8d
LD r16, r13, 0a, 8h
ADD64 r13, r15, r13
ADD64 r14, r14, r16
LD r13, r13, 0a, 8h
ADD64 r13, r13, r14
CP r1, r13
LD r4, r2, 8a, 8h
MULI64 r7, r4, 8d
LD r5, r2, 0a, 8h
ADD64 r10, r7, r2
ADD64 r9, r4, r5
LD r1, r10, 0a, 8h
ADD64 r1, r1, r9
JALA r0, r31, 0a
code size: 246
code size: 231
ret: 8
status: Ok(())

View file

@ -1,8 +1,7 @@
main:
LRA r13, r0, :sin_table
LD r13, r13, 80a, 8h
CP r1, r13
LRA r1, r0, :SIN_TABLE
LD r1, r1, 80a, 8h
JALA r0, r31, 0a
code size: 770
code size: 767
ret: 1736
status: Ok(())

View file

@ -1,14 +1,13 @@
main:
CP r14, r2
LI64 r13, 1d
JNE r14, r13, :0
LI64 r1, 1d
JNE r2, r1, :0
JMP :1
0: JNE r14, r0, :2
LI64 r13, 2d
0: LI64 r7, 0d
JNE r2, r7, :2
LI64 r1, 2d
JMP :1
2: LI64 r13, 3d
1: CP r1, r13
JALA r0, r31, 0a
code size: 75
2: LI64 r1, 3d
1: JALA r0, r31, 0a
code size: 79
ret: 2
status: Ok(())

View file

@ -1,30 +1,25 @@
main:
ADDI64 r254, r254, -24d
ST r31, r254, 0a, 24h
LRA r32, r0, :"abඞ\n\r\t56789\0"
CP r2, r32
ADDI64 r254, r254, -16d
ST r31, r254, 0a, 16h
LRA r2, r0, :"abඞ\n\r\t56789\0"
JAL r31, r0, :str_len
CP r32, r1
LRA r33, r0, :"fff\0"
CP r2, r33
LRA r2, r0, :"fff\0"
JAL r31, r0, :str_len
CP r33, r1
ADD64 r32, r33, r32
CP r1, r32
LD r31, r254, 0a, 24h
ADDI64 r254, r254, 24d
ADD64 r1, r1, r32
LD r31, r254, 0a, 16h
ADDI64 r254, r254, 16d
JALA r0, r31, 0a
str_len:
CP r13, r2
CP r15, r0
CP r14, r15
2: LD r16, r13, 0a, 1h
ANDI r16, r16, 255d
JNE r16, r15, :0
CP r1, r14
LI8 r6, 0b
LI64 r1, 0d
2: LD r8, r2, 0a, 1h
ANDI r8, r8, 255d
ANDI r6, r6, 255d
JNE r8, r6, :0
JMP :1
0: ADDI64 r13, r13, 1d
ADDI64 r14, r14, 1d
0: ADDI64 r2, r2, 1d
ADDI64 r1, r1, 1d
JMP :2
1: JALA r0, r31, 0a
code size: 216

View file

@ -4,10 +4,10 @@ main:
ADDI64 r254, r254, -8d
ST r31, r254, 0a, 8h
JAL r31, r0, :foo
CP r1, r0
LI64 r1, 0d
LD r31, r254, 0a, 8h
ADDI64 r254, r254, 8d
JALA r0, r31, 0a
code size: 88
code size: 95
ret: 0
status: Ok(())

View file

@ -1,65 +0,0 @@
box:
CP r13, r2
CP r1, r13
JALA r0, r31, 0a
main:
ADDI64 r254, r254, -32d
ST r31, r254, 0a, 32h
LI32 r32, 1065353216w
CP r2, r32
JAL r31, r0, :box
CP r33, r1
CP r2, r0
JAL r31, r0, :box
CP r34, r1
FCMPLT32 r33, r33, r34
ANDI r33, r33, 255d
JNE r33, r0, :0
CP r2, r32
JAL r31, r0, :box
CP r33, r1
CP r2, r0
JAL r31, r0, :box
CP r34, r1
FCMPGT32 r33, r33, r34
NOT r33, r33
ANDI r33, r33, 255d
JNE r33, r0, :1
CP r2, r0
JAL r31, r0, :box
CP r33, r1
CP r2, r32
JAL r31, r0, :box
CP r34, r1
FCMPGT32 r33, r33, r34
ANDI r33, r33, 255d
JNE r33, r0, :2
CP r2, r0
JAL r31, r0, :box
CP r33, r1
CP r2, r32
JAL r31, r0, :box
CP r32, r1
FCMPLT32 r32, r33, r32
NOT r32, r32
ANDI r32, r32, 255d
JNE r32, r0, :3
CP r1, r0
JMP :4
3: LI64 r32, 4d
CP r1, r32
JMP :4
2: LI64 r32, 3d
CP r1, r32
JMP :4
1: LI64 r32, 2d
CP r1, r32
JMP :4
0: LI64 r32, 1d
CP r1, r32
4: LD r31, r254, 0a, 32h
ADDI64 r254, r254, 32d
JALA r0, r31, 0a
code size: 355
ret: 0
status: Ok(())

View file

@ -1,8 +1,7 @@
main:
LRA r13, r0, :a
LD r13, r13, 0a, 8h
CP r1, r13
LRA r1, r0, :a
LD r1, r1, 0a, 8h
JALA r0, r31, 0a
code size: 50
code size: 47
ret: 50
status: Ok(())

View file

@ -1,8 +1,7 @@
main:
LRA r13, r0, :a
LD r13, r13, 0a, 8h
CP r1, r13
LRA r1, r0, :a
LD r1, r1, 0a, 8h
JALA r0, r31, 0a
code size: 50
code size: 47
ret: 50
status: Ok(())

View file

@ -1,19 +1,20 @@
cond:
CP r1, r0
LI64 r1, 0d
JALA r0, r31, 0a
main:
ADDI64 r254, r254, -24d
ST r31, r254, 0a, 24h
JAL r31, r0, :cond
CP r32, r0
CP r33, r1
JNE r33, r32, :0
LI64 r32, 0d
CP r33, r32
JNE r1, r33, :0
CP r32, r33
CP r1, r32
JMP :1
0: LI64 r32, 2d
1: CP r1, r32
LD r31, r254, 0a, 24h
0: LI64 r1, 2d
1: LD r31, r254, 0a, 24h
ADDI64 r254, r254, 24d
JALA r0, r31, 0a
code size: 117
code size: 134
ret: 0
status: Ok(())

View file

@ -1,6 +1,6 @@
main:
CP r1, r0
LI64 r1, 0d
JALA r0, r31, 0a
code size: 22
code size: 29
ret: 0
status: Ok(())

View file

@ -1,7 +0,0 @@
main:
LI32 r13, 69w
CP r1, r13
JALA r0, r31, 0a
code size: 28
ret: 69
status: Ok(())

View file

@ -1,6 +1,6 @@
main:
CP r1, r0
LI64 r1, 0d
JALA r0, r31, 0a
code size: 22
code size: 29
ret: 0
status: Ok(())

View file

@ -1,19 +0,0 @@
main:
LI64 r15, 3d
LI64 r16, 10d
CP r14, r0
CP r13, r14
3: JNE r13, r16, :0
LI64 r14, -10d
ADD64 r14, r13, r14
CP r1, r14
JMP :1
0: DIRU64 r0, r17, r13, r15
JNE r17, r14, :2
JMP :2
2: ADDI64 r13, r13, 1d
JMP :3
1: JALA r0, r31, 0a
code size: 103
ret: 0
status: Ok(())

View file

@ -1,11 +1,5 @@
fun:
UN
main:
ADDI64 r254, r254, -8d
ST r31, r254, 0a, 8h
JAL r31, r0, :fun
LD r31, r254, 0a, 8h
ADDI64 r254, r254, 8d
code size: 64
UN
code size: 9
ret: 0
status: Err(Unreachable)

View file

@ -1,72 +0,0 @@
main:
ADDI64 r254, r254, -88d
ST r31, r254, 48a, 40h
LRA r32, r0, :glob_stru
JAL r31, r0, :new_stru
ST r1, r32, 0a, 16h
LD r33, r32, 0a, 8h
JEQ r33, r0, :0
LI64 r32, 300d
CP r1, r32
JMP :1
0: ST r0, r32, 0a, 8h
LD r33, r32, 0a, 8h
JEQ r33, r0, :2
ST r0, r32, 8a, 8h
LI64 r32, 200d
CP r1, r32
JMP :1
2: LI64 r34, 1d
ST r34, r32, 0a, 8h
ST r34, r32, 8a, 8h
ADDI64 r33, r254, 0d
ST r34, r254, 0a, 8h
ST r34, r254, 8a, 8h
ST r34, r254, 16a, 8h
ST r34, r254, 24a, 8h
ST r34, r254, 32a, 8h
ST r34, r254, 40a, 8h
ADDI64 r35, r33, 48d
CP r32, r33
8: JNE r35, r32, :3
LD r32, r254, 32a, 8h
JEQ r32, r0, :4
LI64 r32, 100d
CP r1, r32
JMP :1
4: ST r34, r254, 0a, 8h
ST r34, r254, 8a, 8h
ST r34, r254, 16a, 8h
ST r34, r254, 24a, 8h
ST r34, r254, 32a, 8h
ST r34, r254, 40a, 8h
CP r32, r33
7: LD r34, r254, 32a, 8h
JNE r35, r32, :5
JEQ r34, r0, :6
LI64 r32, 10d
CP r1, r32
JMP :1
6: CP r1, r0
JMP :1
5: ST r0, r32, 0a, 8h
ST r0, r32, 8a, 8h
ADDI64 r32, r32, 16d
JMP :7
3: JAL r31, r0, :new_stru
ST r1, r32, 0a, 16h
ADDI64 r32, r32, 16d
JMP :8
1: LD r31, r254, 48a, 40h
ADDI64 r254, r254, 88d
JALA r0, r31, 0a
new_stru:
ADDI64 r254, r254, -16d
ST r0, r254, 0a, 8h
ST r0, r254, 8a, 8h
LD r1, r254, 0a, 16h
ADDI64 r254, r254, 16d
JALA r0, r31, 0a
code size: 668
ret: 0
status: Ok(())

View file

@ -1,29 +1,30 @@
main:
ADDI64 r254, r254, -12d
LI8 r13, 255b
ST r13, r254, 0a, 1h
ST r0, r254, 1a, 1h
ST r0, r254, 2a, 1h
ST r13, r254, 3a, 1h
ST r0, r254, 4a, 4h
LD r13, r254, 4a, 4h
LI32 r14, 2w
ST r14, r254, 8a, 4h
LD r14, r254, 8a, 4h
LI64 r15, 2d
ANDI r14, r14, 4294967295d
JEQ r14, r15, :0
CP r1, r0
LI8 r1, 255b
ST r1, r254, 0a, 1h
LI8 r4, 0b
ST r4, r254, 1a, 1h
ST r4, r254, 2a, 1h
ST r1, r254, 3a, 1h
LI32 r9, 0w
ST r9, r254, 4a, 4h
LI32 r12, 2w
ST r12, r254, 8a, 4h
LD r3, r254, 8a, 4h
ANDI r3, r3, 4294967295d
ANDI r12, r12, 4294967295d
JEQ r3, r12, :0
LI64 r1, 0d
JMP :1
0: ANDI r13, r13, 4294967295d
JEQ r13, r0, :2
LI64 r13, 64d
CP r1, r13
0: LD r10, r254, 4a, 4h
ANDI r10, r10, 4294967295d
ANDI r9, r9, 4294967295d
JEQ r10, r9, :2
LI64 r1, 64d
JMP :1
2: LI64 r13, 512d
CP r1, r13
2: LI64 r1, 512d
1: ADDI64 r254, r254, 12d
JALA r0, r31, 0a
code size: 235
code size: 257
ret: 512
status: Ok(())

View file

@ -1,21 +1,20 @@
main:
ADDI64 r254, r254, -16d
LI64 r13, 10d
ST r13, r254, 0a, 8h
LI64 r13, 20d
ST r13, r254, 8a, 8h
LI64 r13, 6d
LI64 r14, 5d
LI64 r15, 1d
CP r2, r15
LD r3, r254, 0a, 16h
CP r5, r14
CP r6, r13
LI64 r1, 10d
ADDI64 r4, r254, 0d
ST r1, r254, 0a, 8h
LI64 r7, 20d
ST r7, r254, 8a, 8h
LI64 r6, 6d
LI64 r5, 5d
LI64 r2, 1d
CP r3, r4
LD r3, r3, 0a, 16h
ECA
CP r1, r0
LI64 r1, 0d
ADDI64 r254, r254, 16d
JALA r0, r31, 0a
ev: Ecall
code size: 143
code size: 155
ret: 0
status: Ok(())

View file

@ -1,28 +0,0 @@
main:
ADDI64 r254, r254, -40d
ST r31, r254, 24a, 16h
LI64 r32, 1d
ST r32, r254, 0a, 8h
ST r0, r254, 8a, 8h
ST r0, r254, 16a, 8h
LD r2, r254, 8a, 16h
JAL r31, r0, :pass
CP r32, r1
CP r1, r32
LD r31, r254, 24a, 16h
ADDI64 r254, r254, 40d
JALA r0, r31, 0a
pass:
ADDI64 r254, r254, -16d
ST r2, r254, 0a, 16h
ADDI64 r2, r254, 0d
CP r13, r2
LD r14, r13, 0a, 8h
LD r13, r13, 8a, 8h
ADD64 r13, r13, r14
CP r1, r13
ADDI64 r254, r254, 16d
JALA r0, r31, 0a
code size: 235
ret: 0
status: Ok(())

View file

@ -1,20 +0,0 @@
main:
ADDI64 r254, r254, -16d
ST r31, r254, 0a, 16h
JAL r31, r0, :some_enum
CP r32, r1
ANDI r32, r32, 255d
JNE r32, r0, :0
CP r1, r0
JMP :1
0: LI64 r32, 100d
CP r1, r32
1: LD r31, r254, 0a, 16h
ADDI64 r254, r254, 16d
JALA r0, r31, 0a
some_enum:
CP r1, r0
JALA r0, r31, 0a
code size: 128
ret: 0
status: Ok(())

View file

@ -1,114 +1,105 @@
continue_and_state_change:
CP r13, r2
LI64 r16, 3d
LI64 r17, 2d
LI64 r18, 10d
CP r15, r0
LI64 r14, 4d
6: JLTU r13, r18, :0
LI64 r7, 3d
LI64 r8, 4d
LI64 r9, 2d
LI64 r10, 10d
6: JLTU r2, r10, :0
CP r1, r2
JMP :1
0: JNE r13, r17, :2
CP r13, r14
0: JNE r2, r9, :2
CP r2, r8
JMP :3
2: JNE r13, r16, :4
CP r13, r15
1: CP r1, r13
JMP :5
4: ADDI64 r13, r13, 1d
2: JNE r2, r7, :4
LI64 r1, 0d
1: JMP :5
4: ADDI64 r2, r2, 1d
3: JMP :6
5: JALA r0, r31, 0a
infinite_loop:
ADDI64 r254, r254, -32d
ST r31, r254, 0a, 32h
LI64 r34, 1d
CP r33, r0
CP r32, r33
1: JNE r32, r34, :0
ADDI64 r254, r254, -24d
ST r31, r254, 0a, 24h
LI64 r32, 1d
LI64 r33, 0d
CP r1, r33
1: JNE r1, r32, :0
JMP :0
0: CP r2, r33
JAL r31, r0, :continue_and_state_change
CP r32, r1
JMP :1
LD r31, r254, 0a, 32h
ADDI64 r254, r254, 32d
LD r31, r254, 0a, 24h
ADDI64 r254, r254, 24d
JALA r0, r31, 0a
main:
ADDI64 r254, r254, -40d
ST r31, r254, 0a, 40h
CP r2, r0
ADDI64 r254, r254, -64d
ST r31, r254, 0a, 64h
LI64 r32, 0d
CP r2, r32
JAL r31, r0, :multiple_breaks
LI64 r32, 3d
CP r33, r1
JEQ r33, r32, :0
LI64 r32, 1d
CP r1, r32
LI64 r1, 3d
JEQ r33, r1, :0
LI64 r1, 1d
JMP :1
0: LI64 r33, 4d
CP r2, r33
0: CP r34, r1
LI64 r35, 4d
CP r2, r35
JAL r31, r0, :multiple_breaks
LI64 r34, 10d
CP r35, r1
JEQ r35, r34, :2
LI64 r32, 2d
CP r1, r32
CP r36, r35
LI64 r37, 10d
JEQ r1, r37, :2
LI64 r1, 2d
JMP :1
2: CP r2, r0
2: CP r2, r32
JAL r31, r0, :state_change_in_break
CP r35, r1
JEQ r35, r0, :3
CP r1, r32
JEQ r1, r32, :3
CP r1, r34
JMP :1
3: CP r2, r33
3: CP r2, r36
JAL r31, r0, :state_change_in_break
CP r35, r1
JEQ r35, r34, :4
CP r1, r33
JEQ r1, r37, :4
CP r1, r36
JMP :1
4: CP r2, r34
4: CP r2, r37
JAL r31, r0, :continue_and_state_change
CP r33, r1
JEQ r33, r34, :5
LI64 r32, 5d
CP r1, r32
JEQ r1, r37, :5
LI64 r1, 5d
JMP :1
5: CP r2, r32
5: CP r2, r34
JAL r31, r0, :continue_and_state_change
CP r32, r1
JEQ r32, r0, :6
LI64 r32, 6d
CP r1, r32
JEQ r1, r32, :6
LI64 r1, 6d
JMP :1
6: JAL r31, r0, :infinite_loop
CP r1, r0
1: LD r31, r254, 0a, 40h
ADDI64 r254, r254, 40d
6: CP r38, r32
JAL r31, r0, :infinite_loop
CP r1, r38
1: LD r31, r254, 0a, 64h
ADDI64 r254, r254, 64d
JALA r0, r31, 0a
multiple_breaks:
CP r13, r2
LI64 r14, 3d
LI64 r15, 10d
4: JLTU r13, r15, :0
LI64 r6, 3d
LI64 r5, 10d
4: JLTU r2, r5, :0
CP r1, r2
JMP :1
0: ADDI64 r13, r13, 1d
JNE r13, r14, :2
1: CP r1, r13
JMP :3
2: JMP :4
0: ADDI64 r1, r2, 1d
JNE r1, r6, :2
1: JMP :3
2: CP r2, r1
JMP :4
3: JALA r0, r31, 0a
state_change_in_break:
CP r13, r2
LI64 r14, 3d
LI64 r15, 10d
4: JLTU r13, r15, :0
LI64 r5, 3d
LI64 r6, 10d
4: JLTU r2, r6, :0
CP r1, r2
JMP :1
0: JNE r13, r14, :2
CP r13, r0
1: CP r1, r13
JMP :3
2: ADDI64 r13, r13, 1d
0: JNE r2, r5, :2
LI64 r1, 0d
1: JMP :3
2: ADDI64 r2, r2, 1d
JMP :4
3: JALA r0, r31, 0a
timed out
code size: 667
code size: 668
ret: 10
status: Ok(())

View file

@ -1,55 +1,53 @@
check_platform:
ADDI64 r254, r254, -16d
ST r31, r254, 0a, 16h
ADDI64 r254, r254, -8d
ST r31, r254, 0a, 8h
JAL r31, r0, :x86_fb_ptr
CP r32, r1
CP r1, r32
LD r31, r254, 0a, 16h
ADDI64 r254, r254, 16d
LD r31, r254, 0a, 8h
ADDI64 r254, r254, 8d
JALA r0, r31, 0a
main:
ADDI64 r254, r254, -56d
ST r31, r254, 0a, 56h
ADDI64 r254, r254, -64d
ST r31, r254, 0a, 64h
JAL r31, r0, :check_platform
CP r35, r0
LI64 r36, 30d
LI64 r37, 100d
CP r34, r35
CP r32, r35
CP r33, r35
5: JLTU r34, r36, :0
ADDI64 r32, r32, 1d
CP r2, r35
CP r3, r32
CP r4, r36
LI64 r32, 0d
LI64 r33, 30d
LI64 r34, 100d
CP r35, r32
CP r36, r32
CP r37, r32
5: JLTU r35, r33, :0
ADDI64 r36, r36, 1d
CP r2, r32
CP r3, r36
CP r4, r33
JAL r31, r0, :set_pixel
CP r34, r1
JEQ r34, r33, :1
CP r1, r35
JEQ r1, r37, :1
CP r1, r32
JMP :2
1: JNE r32, r37, :3
CP r1, r33
1: CP r38, r32
JNE r36, r34, :3
CP r1, r37
JMP :2
3: CP r34, r35
3: CP r1, r37
CP r35, r38
JMP :4
0: ADDI64 r33, r33, 1d
ADDI64 r34, r34, 1d
4: JMP :5
2: LD r31, r254, 0a, 56h
ADDI64 r254, r254, 56d
0: CP r1, r37
CP r38, r32
ADDI64 r1, r1, 1d
ADDI64 r35, r35, 1d
4: CP r32, r38
CP r37, r1
JMP :5
2: LD r31, r254, 0a, 64h
ADDI64 r254, r254, 64d
JALA r0, r31, 0a
set_pixel:
CP r13, r2
CP r14, r3
CP r15, r4
MUL64 r14, r14, r15
ADD64 r13, r14, r13
CP r1, r13
MUL64 r7, r3, r4
ADD64 r1, r7, r2
JALA r0, r31, 0a
x86_fb_ptr:
LI64 r13, 100d
CP r1, r13
LI64 r1, 100d
JALA r0, r31, 0a
code size: 329
code size: 330
ret: 3000
status: Ok(())

View file

@ -1,7 +1,6 @@
main:
LI32 r13, 3212836864w
CP r1, r13
LI32 r1, 3212836864w
JALA r0, r31, 0a
code size: 28
code size: 25
ret: 3212836864
status: Ok(())

View file

@ -1,29 +1,21 @@
add_one:
CP r13, r2
ADDI64 r13, r13, 1d
CP r1, r13
ADDI64 r1, r2, 1d
JALA r0, r31, 0a
add_two:
CP r13, r2
ADDI64 r13, r13, 2d
CP r1, r13
ADDI64 r1, r2, 2d
JALA r0, r31, 0a
main:
ADDI64 r254, r254, -24d
ST r31, r254, 0a, 24h
LI64 r32, 10d
CP r2, r32
ADDI64 r254, r254, -16d
ST r31, r254, 0a, 16h
LI64 r2, 10d
JAL r31, r0, :add_one
CP r32, r1
LI64 r33, 20d
CP r2, r33
LI64 r2, 20d
JAL r31, r0, :add_two
CP r33, r1
ADD64 r32, r33, r32
CP r1, r32
LD r31, r254, 0a, 24h
ADDI64 r254, r254, 24d
ADD64 r1, r1, r32
LD r31, r254, 0a, 16h
ADDI64 r254, r254, 16d
JALA r0, r31, 0a
code size: 176
code size: 152
ret: 33
status: Ok(())

View file

@ -1,21 +0,0 @@
b:
CP r13, r3
CP r1, r13
JALA r0, r31, 0a
main:
ADDI64 r254, r254, -32d
ST r31, r254, 8a, 24h
ADDI64 r32, r254, 0d
LI64 r33, 100d
ST r33, r254, 0a, 8h
CP r2, r32
CP r3, r33
JAL r31, r0, :b
CP r32, r1
CP r1, r32
LD r31, r254, 8a, 24h
ADDI64 r254, r254, 32d
JALA r0, r31, 0a
code size: 137
ret: 100
status: Ok(())

View file

@ -1,38 +1,24 @@
add:
CP r13, r2
CP r14, r3
ADD64 r13, r13, r14
CP r1, r13
JALA r0, r31, 0a
add:
CP r13, r2
CP r14, r3
ADD32 r13, r13, r14
CP r1, r13
ADD64 r1, r2, r3
JALA r0, r31, 0a
add:
ADD32 r1, r2, r3
JALA r0, r31, 0a
main:
ADDI64 r254, r254, -32d
ST r31, r254, 0a, 32h
JAL r31, r0, :add
LI32 r32, 2w
CP r2, r32
CP r3, r32
ADDI64 r254, r254, -24d
ST r31, r254, 0a, 24h
LI32 r3, 2w
CP r2, r3
JAL r31, r0, :add
CP r32, r1
LI64 r33, 3d
LI64 r34, 1d
CP r2, r34
CP r3, r33
LI64 r3, 3d
LI64 r2, 1d
JAL r31, r0, :add
CP r33, r1
ANDI r32, r32, 4294967295d
SUB64 r32, r32, r33
CP r1, r32
LD r31, r254, 0a, 32h
ADDI64 r254, r254, 32d
ANDI r33, r32, 4294967295d
SUB64 r1, r33, r1
LD r31, r254, 0a, 24h
ADDI64 r254, r254, 24d
JALA r0, r31, 0a
code size: 209
code size: 158
ret: 0
status: Ok(())

View file

@ -1,32 +0,0 @@
main:
ADDI64 r254, r254, -8d
ST r31, r254, 0a, 8h
JAL r31, r0, :process
LD r31, r254, 0a, 8h
ADDI64 r254, r254, 8d
JALA r0, r31, 0a
opaque:
JALA r0, r31, 0a
process:
ADDI64 r254, r254, -48d
ST r31, r254, 16a, 32h
ADDI64 r33, r254, 0d
ST r0, r254, 0a, 1h
LI64 r32, 1000d
4: JGTU r32, r0, :0
JMP :1
0: CP r2, r33
JAL r31, r0, :opaque
LD r34, r254, 0a, 1h
ANDI r34, r34, 255d
JEQ r34, r0, :2
JMP :3
2: ADDI64 r32, r32, -1d
1: JMP :4
3: LD r31, r254, 16a, 32h
ADDI64 r254, r254, 48d
JALA r0, r31, 0a
timed out
code size: 248
ret: 0
status: Ok(())

View file

@ -1,219 +1,127 @@
deinit:
ADDI64 r254, r254, -40d
ST r31, r254, 0a, 40h
ADDI64 r254, r254, -32d
ST r31, r254, 0a, 32h
CP r32, r2
LD r33, r32, 16a, 8h
LI64 r34, 8d
MUL64 r33, r33, r34
LD r35, r32, 0a, 8h
CP r2, r35
CP r3, r33
CP r4, r34
LD r33, r2, 16a, 8h
LI64 r4, 8d
MUL64 r3, r33, r4
CP r34, r32
LD r2, r34, 0a, 8h
JAL r31, r0, :free
CP r1, r32
JAL r31, r0, :new
LD r31, r254, 0a, 40h
ADDI64 r254, r254, 40d
JALA r0, r31, 0a
deinit:
ADDI64 r254, r254, -40d
ST r31, r254, 0a, 40h
CP r32, r2
LI64 r33, 1d
LD r34, r32, 16a, 8h
LD r35, r32, 0a, 8h
CP r2, r35
CP r3, r34
CP r4, r33
JAL r31, r0, :free
CP r1, r32
JAL r31, r0, :new
LD r31, r254, 0a, 40h
ADDI64 r254, r254, 40d
LD r31, r254, 0a, 32h
ADDI64 r254, r254, 32d
JALA r0, r31, 0a
free:
CP r13, r2
CP r14, r3
CP r15, r4
LRA r16, r0, :free_sys_call
LD r16, r16, 0a, 8h
CP r2, r16
CP r3, r13
CP r4, r14
CP r5, r15
CP r10, r2
LRA r7, r0, :FREE_SYS_CALL
LD r2, r7, 0a, 8h
CP r5, r4
CP r4, r3
CP r3, r10
ECA
CP r13, r1
JALA r0, r31, 0a
main:
ADDI64 r254, r254, -88d
ST r31, r254, 48a, 40h
ADDI64 r32, r254, 24d
ADDI64 r254, r254, -56d
ST r31, r254, 24a, 32h
ADDI64 r32, r254, 0d
CP r1, r32
JAL r31, r0, :new
LI64 r33, 35d
LI64 r3, 69d
CP r2, r32
CP r3, r33
JAL r31, r0, :push
ADDI64 r33, r254, 0d
CP r1, r33
JAL r31, r0, :new
LI8 r34, 34b
CP r2, r33
CP r3, r34
JAL r31, r0, :push
LD r34, r254, 0a, 8h
LD r34, r34, 0a, 1h
LD r35, r254, 24a, 8h
LD r35, r35, 0a, 8h
CP r2, r33
JAL r31, r0, :deinit
LD r33, r254, 0a, 8h
LD r34, r33, 0a, 8h
CP r2, r32
JAL r31, r0, :deinit
ANDI r32, r34, 255d
ADD64 r32, r35, r32
CP r1, r32
LD r31, r254, 48a, 40h
ADDI64 r254, r254, 88d
CP r1, r34
LD r31, r254, 24a, 32h
ADDI64 r254, r254, 56d
JALA r0, r31, 0a
malloc:
CP r13, r2
CP r14, r3
LRA r15, r0, :malloc_sys_call
LD r15, r15, 0a, 8h
CP r2, r15
CP r3, r13
CP r4, r14
CP r9, r2
LRA r5, r0, :MALLOC_SYS_CALL
LD r2, r5, 0a, 8h
CP r4, r3
CP r3, r9
ECA
CP r13, r1
CP r1, r13
JALA r0, r31, 0a
new:
ADDI64 r254, r254, -24d
CP r15, r1
LI64 r14, 8d
ADDI64 r13, r254, 0d
ST r14, r254, 0a, 8h
ST r0, r254, 8a, 8h
ST r0, r254, 16a, 8h
BMC r13, r15, 24h
ADDI64 r254, r254, 24d
JALA r0, r31, 0a
new:
ADDI64 r254, r254, -24d
CP r15, r1
LI64 r14, 1d
ADDI64 r13, r254, 0d
ST r14, r254, 0a, 8h
ST r0, r254, 8a, 8h
ST r0, r254, 16a, 8h
BMC r13, r15, 24h
LI64 r4, 0d
ADDI64 r5, r254, 0d
ST r4, r254, 0a, 8h
ST r4, r254, 8a, 8h
ST r4, r254, 16a, 8h
BMC r5, r1, 24h
ADDI64 r254, r254, 24d
JALA r0, r31, 0a
push:
ADDI64 r254, r254, -80d
ST r31, r254, 0a, 80h
ADDI64 r254, r254, -192d
ST r31, r254, 0a, 192h
CP r32, r3
LI64 r33, 1d
LD r34, r2, 8a, 8h
LD r35, r2, 16a, 8h
CP r36, r2
CP r37, r3
LI64 r35, 1d
LD r33, r36, 8a, 8h
LD r32, r36, 16a, 8h
JNE r32, r33, :0
JNE r32, r0, :1
CP r32, r35
JNE r35, r34, :0
LI64 r37, 0d
JNE r35, r37, :1
CP r38, r33
JMP :2
1: MULI64 r32, r32, 2d
2: CP r2, r32
CP r3, r35
1: MULI64 r38, r35, 2d
2: LI64 r39, 8d
MUL64 r2, r38, r39
CP r3, r39
JAL r31, r0, :malloc
ST r32, r36, 16a, 8h
CP r34, r1
JNE r34, r0, :3
CP r1, r0
CP r40, r1
LI64 r1, 0d
CP r41, r40
JNE r41, r1, :3
JMP :4
3: LD r32, r36, 0a, 8h
ADD64 r38, r33, r32
CP r33, r34
7: LD r39, r36, 0a, 8h
LD r40, r36, 8a, 8h
JNE r38, r32, :5
JEQ r40, r0, :6
CP r2, r39
CP r3, r40
CP r4, r35
3: CP r40, r41
CP r42, r36
ST r38, r42, 16a, 8h
LD r36, r42, 8a, 8h
MULI64 r43, r36, 8d
LD r44, r42, 0a, 8h
ADD64 r45, r44, r43
CP r46, r40
9: LD r2, r42, 0a, 8h
LD r47, r42, 8a, 8h
JNE r45, r44, :5
JEQ r47, r37, :6
CP r4, r39
MUL64 r3, r47, r4
JAL r31, r0, :free
JMP :6
6: ST r34, r36, 0a, 8h
JMP :0
5: LD r39, r32, 0a, 1h
ST r39, r33, 0a, 1h
ADDI64 r33, r33, 1d
ADDI64 r32, r32, 1d
CP r1, r40
JMP :7
0: LD r32, r36, 8a, 8h
LD r33, r36, 0a, 8h
ADD64 r33, r32, r33
ST r37, r33, 0a, 1h
ADD64 r32, r32, r35
ST r32, r36, 8a, 8h
CP r1, r33
4: LD r31, r254, 0a, 80h
ADDI64 r254, r254, 80d
JALA r0, r31, 0a
push:
ADDI64 r254, r254, -88d
ST r31, r254, 0a, 88h
CP r36, r2
CP r37, r3
LI64 r35, 1d
LD r33, r36, 8a, 8h
LD r32, r36, 16a, 8h
JNE r32, r33, :0
JNE r32, r0, :1
CP r32, r35
JMP :2
1: MULI64 r32, r32, 2d
2: LI64 r38, 8d
MUL64 r34, r32, r38
CP r2, r34
CP r3, r38
JAL r31, r0, :malloc
ST r32, r36, 16a, 8h
CP r34, r1
JNE r34, r0, :3
CP r1, r0
JMP :4
3: MULI64 r33, r33, 8d
LD r32, r36, 0a, 8h
ADD64 r39, r32, r33
CP r33, r34
7: LD r40, r36, 0a, 8h
LD r41, r36, 8a, 8h
JNE r39, r32, :5
JEQ r41, r0, :6
MUL64 r32, r41, r38
CP r2, r40
6: CP r1, r40
7: ST r1, r42, 0a, 8h
JMP :8
5: CP r1, r40
CP r4, r39
ADDI64 r41, r46, 8d
ADDI64 r48, r44, 8d
LD r49, r44, 0a, 8h
ST r49, r46, 0a, 8h
CP r44, r48
CP r46, r41
JMP :9
0: CP r42, r36
8: LD r50, r42, 8a, 8h
MULI64 r51, r50, 8d
LD r52, r42, 0a, 8h
ADD64 r1, r52, r51
CP r3, r32
CP r4, r38
JAL r31, r0, :free
JMP :6
6: ST r34, r36, 0a, 8h
JMP :0
5: LD r40, r32, 0a, 8h
ST r40, r33, 0a, 8h
ADDI64 r33, r33, 8d
ADDI64 r32, r32, 8d
JMP :7
0: LD r32, r36, 8a, 8h
MULI64 r33, r32, 8d
LD r34, r36, 0a, 8h
ADD64 r33, r34, r33
ST r37, r33, 0a, 8h
ADD64 r32, r32, r35
ST r32, r36, 8a, 8h
CP r1, r33
4: LD r31, r254, 0a, 88h
ADDI64 r254, r254, 88d
ST r3, r1, 0a, 8h
LD r53, r42, 8a, 8h
ADD64 r54, r53, r33
ST r54, r42, 8a, 8h
4: LD r31, r254, 0a, 192h
ADDI64 r254, r254, 192d
JALA r0, r31, 0a
code size: 1623
code size: 955
ret: 69
status: Ok(())

View file

@ -1,6 +1,7 @@
clobber:
LRA r13, r0, :var
ST r0, r13, 0a, 8h
LRA r1, r0, :var
LI64 r3, 0d
ST r3, r1, 0a, 8h
JALA r0, r31, 0a
main:
ADDI64 r254, r254, -24d
@ -9,11 +10,10 @@ main:
LI64 r33, 2d
ST r33, r32, 0a, 8h
JAL r31, r0, :clobber
LD r32, r32, 0a, 8h
CP r1, r32
LD r1, r32, 0a, 8h
LD r31, r254, 0a, 24h
ADDI64 r254, r254, 24d
JALA r0, r31, 0a
code size: 159
code size: 166
ret: 0
status: Ok(())

View file

@ -1,23 +0,0 @@
inb:
CP r1, r0
JALA r0, r31, 0a
main:
ADDI64 r254, r254, -24d
ST r31, r254, 0a, 24h
LRA r32, r0, :ports
LD r33, r32, 0a, 1h
ANDI r33, r33, 255d
JNE r33, r0, :0
JMP :1
0: JAL r31, r0, :inb
CP r33, r1
CMPU r33, r33, r0
CMPUI r33, r33, 0d
NOT r33, r33
ST r33, r32, 0a, 1h
1: LD r31, r254, 0a, 24h
ADDI64 r254, r254, 24d
JALA r0, r31, 0a
code size: 164
ret: 0
status: Ok(())

View file

@ -1,10 +1,9 @@
main:
LRA r13, r0, :complex_global_var
LD r14, r13, 0a, 8h
ADDI64 r14, r14, 5d
ST r14, r13, 0a, 8h
CP r1, r14
LRA r2, r0, :complex_global_var
LD r3, r2, 0a, 8h
ADDI64 r1, r3, 5d
ST r1, r2, 0a, 8h
JALA r0, r31, 0a
code size: 74
code size: 71
ret: 55
status: Ok(())

View file

@ -1,6 +1,6 @@
main:
CP r1, r0
LI64 r1, 0d
JALA r0, r31, 0a
code size: 22
code size: 29
ret: 0
status: Ok(())

View file

@ -1,20 +1,20 @@
main:
ADDI64 r254, r254, -128d
ADDI64 r14, r254, 0d
LI8 r15, 69b
LI64 r16, 128d
CP r13, r0
2: LD r17, r254, 42a, 1h
JLTU r13, r16, :0
ANDI r13, r17, 255d
CP r1, r13
LI8 r5, 69b
LI64 r6, 128d
LI64 r7, 0d
ADDI64 r4, r254, 0d
2: LD r12, r254, 42a, 1h
JLTU r7, r6, :0
ANDI r1, r12, 255d
JMP :1
0: ADD64 r17, r14, r13
ST r15, r17, 0a, 1h
ADDI64 r13, r13, 1d
0: ADDI64 r3, r7, 1d
ADD64 r7, r4, r7
ST r5, r7, 0a, 1h
CP r7, r3
JMP :2
1: ADDI64 r254, r254, 128d
JALA r0, r31, 0a
code size: 138
code size: 145
ret: 69
status: Ok(())

View file

@ -1,36 +1,30 @@
fib:
ADDI64 r254, r254, -32d
ST r31, r254, 0a, 32h
CP r32, r2
LI64 r33, 1d
LI64 r34, 2d
JGTU r32, r34, :0
CP r1, r33
ADDI64 r254, r254, -40d
ST r31, r254, 0a, 40h
LI64 r1, 1d
LI64 r32, 2d
JGTU r2, r32, :0
JMP :1
0: SUB64 r33, r32, r33
CP r2, r33
0: CP r33, r2
SUB64 r2, r33, r1
CP r34, r33
JAL r31, r0, :fib
CP r33, r1
SUB64 r32, r32, r34
CP r2, r32
CP r2, r34
CP r35, r1
SUB64 r2, r2, r32
JAL r31, r0, :fib
CP r32, r1
ADD64 r32, r32, r33
CP r1, r32
1: LD r31, r254, 0a, 32h
ADDI64 r254, r254, 32d
ADD64 r1, r1, r35
1: LD r31, r254, 0a, 40h
ADDI64 r254, r254, 40d
JALA r0, r31, 0a
main:
ADDI64 r254, r254, -16d
ST r31, r254, 0a, 16h
LI64 r32, 10d
CP r2, r32
ADDI64 r254, r254, -8d
ST r31, r254, 0a, 8h
LI64 r2, 10d
JAL r31, r0, :fib
CP r32, r1
CP r1, r32
LD r31, r254, 0a, 16h
ADDI64 r254, r254, 16d
LD r31, r254, 0a, 8h
ADDI64 r254, r254, 8d
JALA r0, r31, 0a
code size: 229
code size: 211
ret: 55
status: Ok(())

View file

@ -1,9 +1,9 @@
main:
CP r13, r0
0: ADDI64 r13, r13, 1d
LI64 r2, 0d
0: ADDI64 r2, r2, 1d
JMP :0
JALA r0, r31, 0a
timed out
code size: 38
code size: 45
ret: 0
status: Ok(())

View file

@ -1,20 +1,17 @@
main:
LI64 r13, 8d
CP r2, r13
ECA
LI64 r14, 6d
LRA r13, r0, :gb
LD r13, r13, 0a, 8h
CMPU r13, r13, r0
CMPUI r13, r13, 0d
OR r13, r13, r0
ANDI r13, r13, 255d
JNE r13, r0, :0
CP r13, r14
LI64 r7, 6d
LRA r3, r0, :gb
LI64 r6, 0d
LD r8, r3, 0a, 8h
CMPU r9, r8, r6
CMPUI r9, r9, 0d
ORI r11, r9, 0d
ANDI r11, r11, 255d
JNE r11, r0, :0
CP r4, r7
JMP :1
0: LI64 r13, 1d
1: SUB64 r13, r13, r14
CP r1, r13
0: LI64 r4, 1d
1: SUB64 r1, r4, r7
JALA r0, r31, 0a
code size: 131
ret: 0

View file

@ -1,19 +0,0 @@
main:
ADDI64 r254, r254, -72d
ADDI64 r13, r254, 24d
ST r0, r254, 24a, 8h
LI64 r14, 1d
ST r14, r254, 32a, 8h
LI64 r14, 2d
ST r14, r254, 40a, 8h
ADDI64 r14, r254, 0d
BMC r13, r14, 24h
ADDI64 r13, r254, 48d
BMC r14, r13, 24h
LD r13, r254, 48a, 8h
CP r1, r13
ADDI64 r254, r254, 72d
JALA r0, r31, 0a
code size: 159
ret: 0
status: Ok(())

View file

@ -1,29 +1,39 @@
main:
ADDI64 r254, r254, -16d
ST r31, r254, 0a, 16h
ADDI64 r254, r254, -32d
ST r31, r254, 0a, 32h
JAL r31, r0, :scalar_values
CP r32, r1
JEQ r32, r0, :0
LI64 r32, 1d
CP r1, r32
LI64 r32, 0d
CP r33, r32
JEQ r1, r33, :0
LI64 r1, 1d
JMP :1
0: JAL r31, r0, :structs
CP r32, r1
JEQ r32, r0, :2
CP r34, r33
JEQ r1, r34, :2
JAL r31, r0, :structs
CP r32, r1
CP r1, r32
JMP :1
2: CP r1, r0
1: LD r31, r254, 0a, 16h
ADDI64 r254, r254, 16d
2: CP r1, r34
CP r33, r34
1: LD r31, r254, 0a, 32h
ADDI64 r254, r254, 32d
JALA r0, r31, 0a
scalar_values:
CP r1, r0
LI64 r1, 0d
JALA r0, r31, 0a
structs:
CP r1, r0
ADDI64 r254, r254, -32d
LI64 r1, 5d
ST r1, r254, 16a, 8h
ST r1, r254, 24a, 8h
LD r5, r254, 16a, 8h
ADDI64 r7, r5, 15d
ST r7, r254, 0a, 8h
LI64 r10, 20d
ST r10, r254, 8a, 8h
LD r1, r254, 0a, 8h
SUB64 r1, r1, r10
ADDI64 r254, r254, 32d
JALA r0, r31, 0a
code size: 164
code size: 307
ret: 0
status: Ok(())

View file

@ -1,7 +1,6 @@
main:
LI64 r13, 10d
CP r1, r13
LI64 r1, 10d
JALA r0, r31, 0a
code size: 32
code size: 29
ret: 10
status: Ok(())

View file

@ -1,101 +1,109 @@
main:
ADDI64 r254, r254, -98d
ST r31, r254, 58a, 40h
ADDI64 r254, r254, -106d
ST r31, r254, 58a, 48h
ADDI64 r32, r254, 33d
ADDI64 r33, r254, 34d
ADDI64 r2, r254, 34d
ADDI64 r6, r254, 1d
LI64 r33, 0d
ADDI64 r4, r254, 17d
ST r32, r254, 34a, 8h
LI64 r34, 100d
ADDI64 r7, r254, 0d
LI8 r35, 1b
ST r0, r254, 1a, 8h
ST r0, r254, 17a, 8h
ST r33, r254, 1a, 8h
ST r33, r254, 17a, 8h
ST r34, r254, 42a, 8h
LI8 r36, 0b
ST r35, r254, 0a, 1h
ST r0, r254, 9a, 8h
ST r0, r254, 25a, 8h
ST r33, r254, 9a, 8h
ST r33, r254, 25a, 8h
ST r34, r254, 50a, 8h
ST r0, r254, 33a, 1h
CP r2, r33
LD r3, r254, 17a, 16h
LD r5, r254, 1a, 16h
LD r7, r254, 0a, 1h
ST r36, r254, 33a, 1h
CP r3, r4
CP r5, r6
LD r3, r3, 0a, 16h
LD r5, r5, 0a, 16h
LD r7, r7, 0a, 1h
JAL r31, r0, :put_filled_rect
LD r31, r254, 58a, 40h
ADDI64 r254, r254, 98d
LD r31, r254, 58a, 48h
ADDI64 r254, r254, 106d
JALA r0, r31, 0a
put_filled_rect:
ADDI64 r254, r254, -108d
CP r14, r2
ADDI64 r254, r254, -212d
ST r32, r254, 108a, 104h
ST r3, r254, 92a, 16h
ADDI64 r3, r254, 92d
CP r15, r3
ST r5, r254, 76a, 16h
ADDI64 r5, r254, 76d
CP r13, r5
ST r7, r254, 75a, 1h
ADDI64 r7, r254, 75d
CP r16, r7
ADDI64 r17, r254, 25d
LI8 r18, 5b
ST r18, r254, 25a, 1h
LD r19, r13, 0a, 8h
ST r19, r254, 26a, 4h
LI64 r20, 1d
ST r20, r254, 30a, 4h
ST r16, r254, 34a, 8h
LI64 r21, 25d
ADDI64 r22, r254, 50d
ST r18, r254, 50a, 1h
ST r19, r254, 51a, 4h
ST r20, r254, 55a, 4h
ST r16, r254, 59a, 8h
LI64 r23, 2d
LI64 r24, 8d
LD r25, r15, 8a, 8h
LD r13, r13, 8a, 8h
ADD64 r26, r13, r25
SUB64 r26, r26, r20
LD r27, r14, 8a, 8h
MUL64 r26, r27, r26
LD r14, r14, 0a, 8h
ADD64 r26, r14, r26
LD r28, r15, 0a, 8h
MUL64 r15, r27, r25
ADD64 r14, r14, r15
ADD64 r15, r28, r26
ADD64 r14, r28, r14
3: JGTU r13, r20, :0
JNE r13, r20, :1
ADDI64 r13, r254, 0d
ST r18, r254, 0a, 1h
ST r19, r254, 1a, 4h
ST r20, r254, 5a, 4h
ST r16, r254, 9a, 8h
ST r14, r254, 17a, 8h
CP r2, r24
CP r3, r23
CP r4, r13
CP r5, r21
LI64 r8, 25d
LI64 r32, 2d
LI64 r6, 8d
ADDI64 r33, r254, 25d
ADDI64 r34, r254, 50d
LI8 r35, 5b
ST r35, r254, 25a, 1h
LD r36, r5, 0a, 8h
ST r36, r254, 26a, 4h
LI64 r37, 1d
ST r37, r254, 30a, 4h
ST r7, r254, 34a, 8h
ST r35, r254, 50a, 1h
ST r36, r254, 51a, 4h
ST r37, r254, 55a, 4h
ST r7, r254, 59a, 8h
CP r38, r7
LD r7, r3, 8a, 8h
LD r39, r5, 8a, 8h
ADD64 r11, r39, r7
SUB64 r4, r11, r37
LD r40, r2, 8a, 8h
MUL64 r5, r40, r4
LD r9, r2, 0a, 8h
ADD64 r10, r9, r5
LD r2, r3, 0a, 8h
ADD64 r41, r2, r10
MUL64 r3, r40, r7
ADD64 r4, r9, r3
ADD64 r42, r2, r4
3: JGTU r39, r37, :0
JNE r39, r37, :1
ADDI64 r4, r254, 0d
ST r35, r254, 0a, 1h
ST r36, r254, 1a, 4h
ST r37, r254, 5a, 4h
ST r38, r254, 9a, 8h
ST r42, r254, 17a, 8h
CP r2, r6
CP r3, r32
CP r5, r8
ECA
JMP :1
1: JMP :2
0: ST r14, r254, 67a, 8h
CP r2, r24
CP r3, r23
CP r4, r22
CP r5, r21
0: CP r3, r32
CP r43, r6
CP r44, r8
ST r42, r254, 67a, 8h
CP r2, r43
CP r4, r34
CP r5, r44
ECA
ST r15, r254, 42a, 8h
CP r2, r24
CP r3, r23
CP r4, r17
CP r5, r21
ST r41, r254, 42a, 8h
CP r2, r43
CP r3, r32
CP r4, r33
CP r5, r44
ECA
SUB64 r13, r13, r23
SUB64 r15, r15, r27
ADD64 r14, r27, r14
ADD64 r42, r40, r42
SUB64 r41, r41, r40
SUB64 r39, r39, r32
CP r6, r43
CP r8, r44
JMP :3
2: ADDI64 r254, r254, 108d
2: LD r32, r254, 108a, 104h
ADDI64 r254, r254, 212d
JALA r0, r31, 0a
code size: 842
code size: 917
ret: 0
status: Ok(())

View file

@ -0,0 +1,8 @@
main:
LRA r2, r0, :x
LI64 r1, 0d
ST r1, r2, 0a, 8h
JALA r0, r31, 0a
code size: 57
ret: 0
status: Ok(())

View file

@ -1,25 +1,20 @@
main:
ADDI64 r254, r254, -48d
ST r31, r254, 16a, 32h
ADDI64 r32, r254, 0d
ADDI64 r33, r254, 8d
ST r0, r254, 0a, 8h
ST r0, r254, 8a, 8h
LI64 r34, 1024d
CP r2, r33
CP r3, r32
CP r4, r34
ADDI64 r254, r254, -32d
ST r31, r254, 16a, 16h
ADDI64 r3, r254, 0d
ADDI64 r2, r254, 8d
LI64 r32, 0d
ST r32, r254, 0a, 8h
ST r32, r254, 8a, 8h
LI64 r4, 1024d
JAL r31, r0, :set
CP r32, r1
ANDI r32, r32, 4294967295d
CP r1, r32
LD r31, r254, 16a, 32h
ADDI64 r254, r254, 48d
ANDI r1, r1, 4294967295d
LD r31, r254, 16a, 16h
ADDI64 r254, r254, 32d
JALA r0, r31, 0a
set:
CP r13, r4
CP r1, r13
CP r1, r4
JALA r0, r31, 0a
code size: 175
code size: 167
ret: 1024
status: Ok(())

View file

@ -1,28 +1,29 @@
integer_range:
CP r13, r2
CP r14, r3
LI64 r15, 4d
LI64 r16, 3d
CP r2, r16
CP r3, r15
ECA
SUB64 r14, r14, r13
ADDI64 r14, r14, 1d
CP r15, r1
DIRU64 r0, r14, r15, r14
ADD64 r13, r14, r13
CP r1, r13
JALA r0, r31, 0a
main:
ADDI64 r254, r254, -16d
ST r31, r254, 0a, 16h
LI64 r32, 1000d
CP r2, r0
CP r3, r32
JAL r31, r0, :integer_range
LD r31, r254, 0a, 16h
ST r32, r254, 0a, 16h
CP r32, r2
CP r33, r3
LI64 r3, 4d
LI64 r2, 3d
ECA
CP r2, r32
CP r3, r33
SUB64 r11, r3, r2
ADDI64 r3, r11, 1d
DIRU64 r0, r3, r1, r3
ADD64 r1, r3, r2
LD r32, r254, 0a, 16h
ADDI64 r254, r254, 16d
JALA r0, r31, 0a
code size: 164
main:
ADDI64 r254, r254, -8d
ST r31, r254, 0a, 8h
LI64 r3, 1000d
LI64 r2, 0d
JAL r31, r0, :integer_range
LD r31, r254, 0a, 8h
ADDI64 r254, r254, 8d
JALA r0, r31, 0a
code size: 210
ret: 42
status: Ok(())

View file

@ -1,49 +0,0 @@
chars:
ADDI64 r254, r254, -32d
ST r3, r254, 16a, 16h
ADDI64 r3, r254, 16d
CP r13, r3
ADDI64 r14, r254, 0d
BMC r13, r14, 16h
LD r1, r14, 0a, 16h
ADDI64 r254, r254, 32d
JALA r0, r31, 0a
main:
ADDI64 r254, r254, -56d
ST r31, r254, 32a, 24h
LRA r32, r0, :Hello, World!
ST r32, r254, 16a, 8h
LI64 r32, 13d
ST r32, r254, 24a, 8h
ADDI64 r32, r254, 0d
LD r3, r254, 16a, 16h
JAL r31, r0, :chars
ST r1, r32, 0a, 16h
2: CP r2, r32
JAL r31, r0, :next
CP r33, r1
ANDI r33, r33, 65535d
JNE r33, r0, :0
JMP :1
0: JMP :2
1: LD r31, r254, 32a, 24h
ADDI64 r254, r254, 56d
JALA r0, r31, 0a
next:
CP r13, r2
LD r14, r13, 8a, 8h
JNE r14, r0, :0
CP r1, r0
JMP :1
0: LD r15, r13, 0a, 8h
ADDI64 r15, r15, 1d
ST r15, r13, 0a, 8h
ADDI64 r14, r14, -1d
LD r15, r15, 0a, 1h
ST r14, r13, 8a, 8h
ORI r13, r15, 32768d
CP r1, r13
1: JALA r0, r31, 0a
code size: 423
ret: 0
status: Ok(())

View file

@ -1,16 +1,16 @@
main:
ADDI64 r254, r254, -8d
LI64 r13, 10d
ST r13, r254, 0a, 8h
2: LD r13, r254, 0a, 8h
JNE r13, r0, :0
CP r1, r13
LI64 r3, 0d
LI64 r2, 10d
ST r2, r254, 0a, 8h
2: LD r1, r254, 0a, 8h
JNE r1, r3, :0
JMP :1
0: ADDI64 r13, r13, -1d
ST r13, r254, 0a, 8h
0: ADDI64 r11, r1, -1d
ST r11, r254, 0a, 8h
JMP :2
1: ADDI64 r254, r254, 8d
JALA r0, r31, 0a
code size: 119
code size: 126
ret: 0
status: Ok(())

View file

@ -1,28 +1,25 @@
fib:
CP r13, r2
LI64 r17, 1d
CP r15, r0
CP r14, r15
CP r16, r17
2: JNE r13, r15, :0
CP r1, r14
LI64 r4, 1d
LI64 r5, 0d
CP r1, r5
CP r10, r4
2: JNE r2, r5, :0
JMP :1
0: SUB64 r13, r13, r17
ADD64 r14, r16, r14
SWA r14, r16
0: ADD64 r1, r10, r1
SUB64 r2, r2, r4
CP r3, r1
CP r1, r10
CP r10, r3
JMP :2
1: JALA r0, r31, 0a
main:
ADDI64 r254, r254, -16d
ST r31, r254, 0a, 16h
LI64 r32, 10d
CP r2, r32
ADDI64 r254, r254, -8d
ST r31, r254, 0a, 8h
LI64 r2, 10d
JAL r31, r0, :fib
CP r32, r1
CP r1, r32
LD r31, r254, 0a, 16h
ADDI64 r254, r254, 16d
LD r31, r254, 0a, 8h
ADDI64 r254, r254, 8d
JALA r0, r31, 0a
code size: 155
code size: 153
ret: 55
status: Ok(())

View file

@ -1,7 +1,6 @@
main:
LI64 r13, 1d
CP r1, r13
LI64 r1, 1d
JALA r0, r31, 0a
code size: 32
code size: 29
ret: 1
status: Ok(())

View file

@ -1,36 +0,0 @@
decide:
ADDI64 r254, r254, -24d
CP r14, r2
CP r15, r1
ADDI64 r13, r254, 0d
ST r14, r254, 0a, 8h
ST r0, r254, 8a, 8h
ST r0, r254, 16a, 8h
BMC r13, r15, 24h
ADDI64 r254, r254, 24d
JALA r0, r31, 0a
main:
ADDI64 r254, r254, -104d
ST r31, r254, 72a, 32h
ADDI64 r32, r254, 48d
CP r1, r32
CP r2, r0
JAL r31, r0, :decide
ADDI64 r33, r254, 24d
BMC r32, r33, 24h
LI64 r34, 1d
CP r1, r33
CP r2, r34
JAL r31, r0, :decide
ADDI64 r34, r254, 0d
BMC r32, r34, 24h
LD r32, r254, 24a, 8h
LD r33, r254, 0a, 8h
ADD64 r32, r33, r32
CP r1, r32
LD r31, r254, 72a, 32h
ADDI64 r254, r254, 104d
JALA r0, r31, 0a
code size: 273
ret: 1
status: Ok(())

View file

@ -1,59 +0,0 @@
main:
ADDI64 r254, r254, -72d
ST r31, r254, 32a, 40h
LRA r32, r0, :"Goodbye, World!\0"
LRA r33, r0, :"Hello, World!\0"
ST r32, r254, 16a, 8h
ST r33, r254, 24a, 8h
LD r2, r254, 24a, 8h
LD r3, r254, 16a, 8h
JAL r31, r0, :print
ADDI64 r34, r254, 8d
ADDI64 r35, r254, 0d
ST r32, r254, 8a, 8h
ST r33, r254, 0a, 8h
CP r2, r35
CP r3, r34
JAL r31, r0, :print2
LD r31, r254, 32a, 40h
ADDI64 r254, r254, 72d
JALA r0, r31, 0a
print:
ADDI64 r254, r254, -16d
ST r2, r254, 8a, 8h
ADDI64 r2, r254, 8d
CP r13, r2
ST r3, r254, 0a, 8h
ADDI64 r3, r254, 0d
CP r14, r3
LD r13, r13, 0a, 8h
LI64 r15, 37d
CP r2, r15
CP r3, r13
ECA
LD r13, r14, 0a, 8h
CP r2, r15
CP r3, r13
ECA
ADDI64 r254, r254, 16d
JALA r0, r31, 0a
print2:
CP r13, r2
CP r14, r3
LD r13, r13, 0a, 8h
LI64 r15, 37d
CP r2, r15
CP r3, r13
ECA
LD r13, r14, 0a, 8h
CP r2, r15
CP r3, r13
ECA
JALA r0, r31, 0a
Hello, World!
Goodbye, World!
Hello, World!
Goodbye, World!
code size: 435
ret: 0
status: Ok(())

View file

@ -1,23 +1,27 @@
main:
ADDI64 r254, r254, -24d
ST r31, r254, 0a, 24h
ADDI64 r254, r254, -32d
ST r31, r254, 0a, 32h
JAL r31, r0, :opaque
CP r33, r1
CP r32, r1
JAL r31, r0, :opaque
JNE r33, r0, :0
CP r32, r0
LI64 r33, 0d
CP r1, r32
JNE r1, r33, :0
CP r32, r1
LI64 r1, 0d
CP r34, r32
JMP :1
0: LD r32, r33, 0a, 8h
1: JEQ r33, r0, :2
LD r32, r33, 0a, 8h
0: CP r34, r1
LD r1, r34, 0a, 8h
1: JEQ r34, r33, :2
LD r1, r34, 0a, 8h
JMP :2
2: CP r1, r32
LD r31, r254, 0a, 24h
ADDI64 r254, r254, 24d
2: LD r31, r254, 0a, 32h
ADDI64 r254, r254, 32d
JALA r0, r31, 0a
opaque:
CP r1, r0
LI64 r1, 0d
JALA r0, r31, 0a
code size: 150
code size: 183
ret: 0
status: Ok(())

View file

@ -1,6 +1,6 @@
test.hb:4:18: unwrap is not needed since the value is (provably) never null, remove it, or replace with '@as(<expr_ty>, <opt_expr>)'
ptr1 := @unwrap(always_nn)
^
test.hb:6:18: unwrap is incorrect since the value is (provably) always null, make sure your logic is correct
ptr2 := @unwrap(always_n)
^
test.hb:4:17: unwrap is not needed since the value is (provably) never null, remove it, or replace with '@as(<expr_ty>, <opt_expr>)'
ptr := @unwrap(always_nn)
^
test.hb:6:16: unwrap is incorrect since the value is (provably) always null, make sure your logic is correct
ptr = @unwrap(always_n)
^

View file

@ -1,31 +0,0 @@
main:
ADDI64 r254, r254, -30d
ST r31, r254, 6a, 24h
ADDI64 r32, r254, 0d
2: JAL r31, r0, :return_fn
ST r1, r32, 0a, 6h
LD r33, r254, 0a, 1h
ANDI r33, r33, 255d
JEQ r33, r0, :0
LI64 r32, 1d
CP r1, r32
JMP :1
0: JMP :2
1: LD r31, r254, 6a, 24h
ADDI64 r254, r254, 30d
JALA r0, r31, 0a
return_fn:
ADDI64 r254, r254, -6d
LI8 r13, 1b
ST r13, r254, 0a, 1h
ST r0, r254, 1a, 1h
ST r0, r254, 2a, 1h
ST r0, r254, 3a, 1h
ST r0, r254, 4a, 1h
ST r0, r254, 5a, 1h
LD r1, r254, 0a, 6h
ADDI64 r254, r254, 6d
JALA r0, r31, 0a
code size: 277
ret: 1
status: Ok(())

View file

@ -1,72 +0,0 @@
foo:
ADDI64 r254, r254, -112d
ST r31, r254, 80a, 32h
ADDI64 r32, r254, 64d
LRA r33, r0, :some_file
CP r3, r33
JAL r31, r0, :get
ST r1, r32, 0a, 16h
LD r33, r254, 64a, 1h
ANDI r33, r33, 255d
JNE r33, r0, :0
ST r0, r254, 48a, 1h
LD r1, r254, 48a, 16h
JMP :1
0: LI8 r33, 1b
LI64 r34, 4d
LD r32, r254, 72a, 8h
JNE r32, r34, :2
ST r33, r254, 32a, 1h
LI64 r32, 2d
ST r32, r254, 40a, 8h
LD r1, r254, 32a, 16h
JMP :1
2: LRA r34, r0, :magic
LD r34, r34, 0a, 8h
JNE r34, r32, :3
ST r33, r254, 16a, 1h
ST r0, r254, 24a, 8h
LD r1, r254, 16a, 16h
JMP :1
3: ST r0, r254, 0a, 1h
LD r1, r254, 0a, 16h
1: LD r31, r254, 80a, 32h
ADDI64 r254, r254, 112d
JALA r0, r31, 0a
get:
ADDI64 r254, r254, -32d
CP r13, r3
LD r13, r13, 0a, 1h
LRA r14, r0, :magic
ANDI r13, r13, 255d
LD r14, r14, 0a, 8h
JNE r14, r13, :0
LI8 r13, 1b
ST r13, r254, 16a, 1h
ST r14, r254, 24a, 8h
LD r1, r254, 16a, 16h
JMP :1
0: ST r0, r254, 0a, 1h
LD r1, r254, 0a, 16h
1: ADDI64 r254, r254, 32d
JALA r0, r31, 0a
main:
ADDI64 r254, r254, -40d
ST r31, r254, 16a, 24h
ADDI64 r32, r254, 0d
JAL r31, r0, :foo
ST r1, r32, 0a, 16h
LD r33, r254, 0a, 1h
ANDI r33, r33, 255d
JNE r33, r0, :0
LI64 r32, 100d
CP r1, r32
JMP :1
0: LD r32, r254, 8a, 8h
CP r1, r32
1: LD r31, r254, 16a, 24h
ADDI64 r254, r254, 40d
JALA r0, r31, 0a
code size: 673
ret: 0
status: Ok(())

View file

@ -1,24 +1,26 @@
get_ptr:
CP r1, r0
ADDI64 r254, r254, -8d
ADDI64 r1, r254, 0d
ADDI64 r254, r254, 8d
JALA r0, r31, 0a
main:
ADDI64 r254, r254, -32d
ST r31, r254, 0a, 32h
ADDI64 r254, r254, -40d
ST r31, r254, 0a, 40h
JAL r31, r0, :get_ptr
CP r32, r1
JNE r32, r0, :0
CP r1, r0
LI64 r32, 0d
JNE r1, r32, :0
LI64 r1, 0d
JMP :1
0: LI64 r33, 10d
3: LD r34, r32, 0a, 8h
JEQ r34, r33, :2
ADDI64 r34, r34, 1d
ST r34, r32, 0a, 8h
JMP :3
2: CP r1, r34
1: LD r31, r254, 0a, 32h
ADDI64 r254, r254, 32d
CP r34, r1
2: LD r1, r34, 0a, 8h
JEQ r1, r33, :1
ADDI64 r35, r1, 1d
ST r35, r34, 0a, 8h
JMP :2
1: LD r31, r254, 0a, 40h
ADDI64 r254, r254, 40d
JALA r0, r31, 0a
code size: 164
ret: 0
code size: 208
ret: 10
status: Ok(())

View file

@ -1,57 +1,65 @@
main:
ADDI64 r254, r254, -58d
ST r31, r254, 26a, 32h
ADDI64 r254, r254, -122d
ST r31, r254, 26a, 96h
JAL r31, r0, :returner_fn
CP r32, r1
ADDI64 r33, r254, 2d
CP r1, r33
ADDI64 r1, r254, 2d
JAL r31, r0, :returner_bn
ADDI64 r34, r254, 0d
ADDI64 r33, r254, 0d
JAL r31, r0, :returner_cn
ST r1, r34, 0a, 2h
LD r33, r254, 2a, 1h
CMPU r32, r32, r0
CMPUI r32, r32, 0d
CMPU r33, r33, r0
CMPUI r33, r33, 0d
LD r34, r254, 0a, 1h
AND r32, r33, r32
CMPU r33, r34, r0
CMPUI r33, r33, 0d
AND r32, r33, r32
ANDI r32, r32, 255d
JNE r32, r0, :0
CP r1, r0
JMP :1
0: LI64 r32, 1d
ST r1, r254, 0a, 2h
LI8 r34, 0b
LI8 r35, 0b
LD r36, r254, 2a, 1h
CP r1, r32
1: LD r31, r254, 26a, 32h
ADDI64 r254, r254, 58d
ANDI r37, r37, 255d
ANDI r1, r1, 255d
CMPU r37, r1, r34
CMPUI r37, r37, 0d
ANDI r38, r38, 255d
ANDI r36, r36, 255d
CMPU r38, r36, r35
CMPUI r38, r38, 0d
LD r39, r254, 0a, 1h
AND r40, r38, r37
ANDI r41, r41, 255d
ANDI r39, r39, 255d
CMPU r41, r39, r35
CMPUI r41, r41, 0d
AND r42, r41, r40
ANDI r42, r42, 255d
JNE r42, r0, :0
LI64 r1, 0d
JMP :1
0: LI64 r1, 1d
1: LD r31, r254, 26a, 96h
ADDI64 r254, r254, 122d
JALA r0, r31, 0a
returner_bn:
ADDI64 r254, r254, -24d
CP r15, r1
LI8 r14, 1b
ADDI64 r13, r254, 0d
ST r14, r254, 0a, 1h
ST r0, r254, 8a, 8h
ST r0, r254, 16a, 8h
BMC r13, r15, 24h
LI8 r6, 1b
ADDI64 r5, r254, 0d
ST r6, r254, 0a, 1h
LI64 r6, 0d
ST r6, r254, 8a, 8h
ST r6, r254, 16a, 8h
BMC r5, r1, 24h
ADDI64 r254, r254, 24d
JALA r0, r31, 0a
returner_cn:
ADDI64 r254, r254, -2d
LI8 r13, 1b
ST r13, r254, 0a, 1h
ST r0, r254, 1a, 1h
LD r1, r254, 0a, 2h
LI8 r4, 1b
ADDI64 r3, r254, 0d
ST r4, r254, 0a, 1h
LI8 r4, 0b
ST r4, r254, 1a, 1h
LD r1, r3, 0a, 2h
ADDI64 r254, r254, 2d
JALA r0, r31, 0a
returner_fn:
LD r13, r254, 0a, 0h
ORI r13, r13, 128d
CP r1, r13
LD r1, r254, 0a, 0h
ORI r1, r1, 128d
JALA r0, r31, 0a
code size: 452
code size: 546
ret: 1
status: Ok(())

View file

@ -1,144 +1,135 @@
decide:
LI8 r13, 1b
CP r1, r13
LI8 r1, 1b
JALA r0, r31, 0a
main:
ADDI64 r254, r254, -136d
ST r31, r254, 96a, 40h
ADDI64 r254, r254, -224d
ST r31, r254, 80a, 144h
JAL r31, r0, :decide
CP r33, r0
ADDI64 r34, r254, 88d
CP r32, r1
ANDI r32, r32, 255d
JNE r32, r0, :0
CP r32, r33
LI64 r32, 0d
ADDI64 r2, r254, 72d
CP r33, r2
ANDI r1, r1, 255d
JNE r1, r0, :0
CP r34, r32
JMP :1
0: CP r32, r34
1: JNE r32, r33, :2
LI64 r32, 9001d
CP r1, r32
0: CP r34, r33
1: JNE r34, r32, :2
LI64 r1, 9001d
JMP :3
2: JAL r31, r0, :decide
CP r33, r1
ANDI r33, r33, 255d
JNE r33, r0, :4
LI8 r33, 1b
ST r33, r254, 72a, 1h
LD r32, r32, 0a, 8h
ST r32, r254, 80a, 8h
LI8 r35, 0b
ANDI r1, r1, 255d
JNE r1, r0, :4
LI8 r36, 1b
ST r36, r254, 56a, 1h
LD r36, r34, 0a, 8h
ST r36, r254, 64a, 8h
JMP :5
4: ST r0, r254, 72a, 1h
5: LD r32, r254, 72a, 1h
ANDI r32, r32, 255d
JEQ r32, r0, :6
LI64 r32, 42d
CP r1, r32
4: ST r35, r254, 56a, 1h
5: LD r37, r254, 56a, 1h
ANDI r37, r37, 255d
ANDI r35, r35, 255d
JEQ r37, r35, :6
LI64 r1, 42d
JMP :3
6: JAL r31, r0, :decide
CP r33, r0
CP r32, r1
ANDI r32, r32, 255d
JNE r32, r0, :7
CP r32, r33
LI32 r38, 0w
ANDI r1, r1, 255d
JNE r1, r0, :7
CP r39, r38
JMP :8
7: LI32 r32, 2147483649w
8: ANDI r32, r32, 4294967295d
ANDI r33, r33, 4294967295d
JNE r32, r33, :9
LI64 r32, 69d
CP r1, r32
7: LI32 r39, 2147483649w
8: ANDI r39, r39, 4294967295d
ANDI r38, r38, 4294967295d
JNE r39, r38, :9
LI64 r1, 69d
JMP :3
9: ADDI64 r33, r254, 56d
9: ADDI64 r3, r254, 40d
CP r40, r3
JAL r31, r0, :new_foo
ST r1, r33, 0a, 16h
LD r35, r254, 56a, 8h
JNE r35, r0, :10
LI64 r32, 999d
CP r1, r32
ST r1, r254, 40a, 16h
LI64 r32, 0d
LD r41, r254, 40a, 8h
JNE r41, r32, :10
LI64 r1, 999d
JMP :3
10: LRA r35, r0, :"foo\0"
ST r35, r254, 40a, 8h
LI64 r35, 4d
ST r35, r254, 48a, 8h
LD r2, r33, 0a, 16h
LD r4, r254, 40a, 16h
10: LRA r4, r0, :"foo\0"
CP r3, r40
CP r2, r3
LD r2, r2, 0a, 16h
JAL r31, r0, :use_foo
ADDI64 r33, r254, 0d
ADDI64 r42, r254, 0d
JAL r31, r0, :no_foo
ST r1, r33, 0a, 16h
ST r1, r254, 0a, 16h
JAL r31, r0, :decide
CP r35, r1
ANDI r35, r35, 255d
JNE r35, r0, :11
ANDI r1, r1, 255d
JNE r1, r0, :11
CP r2, r33
JMP :12
11: ST r34, r254, 0a, 8h
LI64 r35, 1d
ST r35, r254, 8a, 8h
ST r35, r254, 88a, 8h
12: LD r35, r254, 0a, 8h
JNE r35, r0, :13
LI64 r32, 34d
CP r1, r32
11: CP r2, r33
ST r2, r254, 0a, 8h
LI64 r43, 1d
ST r43, r254, 8a, 8h
ST r43, r254, 72a, 8h
12: LD r44, r254, 0a, 8h
JNE r44, r32, :13
LI64 r1, 34d
JMP :3
13: ADDI64 r35, r254, 16d
CP r1, r35
CP r2, r34
13: ADDI64 r1, r254, 16d
JAL r31, r0, :new_bar
JAL r31, r0, :decide
CP r34, r1
ANDI r34, r34, 255d
JNE r34, r0, :14
ANDI r1, r1, 255d
JNE r1, r0, :14
JMP :15
14: ST r0, r254, 16a, 1h
15: LD r34, r254, 16a, 1h
ANDI r34, r34, 255d
JEQ r34, r0, :16
LI64 r32, 420d
CP r1, r32
14: ST r35, r254, 16a, 1h
15: LD r45, r254, 16a, 1h
ANDI r45, r45, 255d
ANDI r35, r35, 255d
JEQ r45, r35, :16
LI64 r1, 420d
JMP :3
16: LD r33, r254, 0a, 8h
LD r33, r33, 0a, 8h
ANDI r32, r32, 65535d
SUB64 r32, r32, r33
CP r1, r32
3: LD r31, r254, 96a, 40h
ADDI64 r254, r254, 136d
16: LD r46, r254, 0a, 8h
LD r47, r46, 0a, 8h
ANDI r48, r39, 65535d
SUB64 r1, r48, r47
3: LD r31, r254, 80a, 144h
ADDI64 r254, r254, 224d
JALA r0, r31, 0a
new_bar:
ADDI64 r254, r254, -24d
CP r14, r2
CP r16, r1
LI8 r15, 1b
ADDI64 r13, r254, 0d
ST r15, r254, 0a, 1h
ST r14, r254, 8a, 8h
LI64 r14, 1d
ST r14, r254, 16a, 8h
BMC r13, r16, 24h
LI8 r8, 1b
ADDI64 r7, r254, 0d
ST r8, r254, 0a, 1h
ST r2, r254, 8a, 8h
LI64 r9, 1d
ST r9, r254, 16a, 8h
BMC r7, r1, 24h
ADDI64 r254, r254, 24d
JALA r0, r31, 0a
new_foo:
ADDI64 r254, r254, -24d
ADDI64 r13, r254, 0d
ST r13, r254, 8a, 8h
ST r0, r254, 16a, 8h
LD r1, r254, 8a, 16h
ADDI64 r3, r254, 0d
ADDI64 r2, r254, 8d
ST r3, r254, 8a, 8h
LI64 r5, 0d
ST r5, r254, 16a, 8h
LD r1, r2, 0a, 16h
ADDI64 r254, r254, 24d
JALA r0, r31, 0a
no_foo:
ADDI64 r254, r254, -16d
ST r0, r254, 0a, 8h
LD r1, r254, 0a, 16h
ADDI64 r1, r254, 0d
LI64 r3, 0d
ST r3, r254, 0a, 8h
LD r1, r1, 0a, 16h
ADDI64 r254, r254, 16d
JALA r0, r31, 0a
use_foo:
ADDI64 r254, r254, -32d
ST r2, r254, 16a, 16h
ADDI64 r2, r254, 16d
ST r4, r254, 0a, 16h
ADDI64 r4, r254, 0d
ADDI64 r254, r254, 32d
ADDI64 r254, r254, -16d
ST r2, r254, 0a, 16h
ADDI64 r2, r254, 0d
ADDI64 r254, r254, 16d
JALA r0, r31, 0a
code size: 1162
code size: 1143
ret: 0
status: Ok(())

View file

@ -1,34 +1,30 @@
inb:
CP r13, r2
CP r1, r13
CP r1, r2
JALA r0, r31, 0a
main:
ADDI64 r254, r254, -24d
ST r31, r254, 0a, 24h
LI64 r32, 100d
4: CP r2, r32
ADDI64 r254, r254, -32d
ST r31, r254, 0a, 32h
LI64 r32, 0d
LI64 r33, 100d
4: CP r2, r33
JAL r31, r0, :inb
CP r33, r1
ANDI r33, r33, 2d
JNE r33, r0, :0
LI64 r33, 96d
CP r2, r33
CP r3, r0
ANDI r34, r1, 2d
JNE r34, r32, :0
LI64 r2, 96d
CP r3, r32
JAL r31, r0, :outb
3: CP r2, r32
3: CP r2, r33
JAL r31, r0, :inb
CP r33, r1
JEQ r33, r0, :1
LI64 r32, 1d
CP r1, r32
JEQ r1, r32, :1
LI64 r1, 1d
JMP :2
1: JMP :3
0: JMP :4
2: LD r31, r254, 0a, 24h
ADDI64 r254, r254, 24d
2: LD r31, r254, 0a, 32h
ADDI64 r254, r254, 32d
JALA r0, r31, 0a
outb:
JALA r0, r31, 0a
code size: 203
code size: 198
ret: 1
status: Ok(())

View file

@ -1,21 +1,22 @@
main:
ADDI64 r254, r254, -16d
ADDI64 r13, r254, 0d
CP r3, r0
CP r4, r0
CP r5, r0
CP r6, r0
ADDI64 r3, r254, 0d
LI64 r6, 0d
CP r3, r6
CP r4, r6
CP r5, r6
ECA
ST r1, r13, 0a, 16h
LD r14, r254, 0a, 1h
ANDI r14, r14, 255d
JNE r14, r0, :0
ST r1, r254, 0a, 16h
LI8 r8, 0b
LD r9, r254, 0a, 1h
ANDI r9, r9, 255d
ANDI r8, r8, 255d
JNE r9, r8, :0
UN
0: LD r13, r254, 8a, 8h
CP r1, r13
0: LD r1, r254, 8a, 8h
ADDI64 r254, r254, 16d
JALA r0, r31, 0a
unknown ecall: 0
code size: 124
code size: 142
ret: 0
status: Err(Unreachable)

View file

@ -1,32 +1,34 @@
main:
ADDI64 r254, r254, -56d
ST r31, r254, 24a, 32h
ADDI64 r32, r254, 0d
LI64 r33, 1d
ST r33, r254, 16a, 8h
LI64 r34, 4d
ST r34, r254, 0a, 8h
ST r33, r254, 8a, 8h
ADDI64 r254, r254, -104d
ST r31, r254, 40a, 64h
LI64 r32, 4d
ADDI64 r33, r254, 24d
ADDI64 r34, r254, 0d
ST r32, r254, 24a, 8h
LI64 r35, 1d
ST r35, r254, 32a, 8h
ST r35, r254, 16a, 8h
BMC r33, r34, 16h
JAL r31, r0, :opaque
ST r1, r32, 0a, 16h
LD r33, r254, 8a, 8h
LD r34, r254, 16a, 8h
ADD64 r33, r34, r33
LD r32, r254, 0a, 8h
SUB64 r32, r32, r33
CP r1, r32
LD r31, r254, 24a, 32h
ADDI64 r254, r254, 56d
ST r1, r254, 0a, 16h
LD r36, r254, 8a, 8h
LD r37, r254, 16a, 8h
ADD64 r38, r37, r36
LD r37, r254, 0a, 8h
SUB64 r1, r37, r38
LD r31, r254, 40a, 64h
ADDI64 r254, r254, 104d
JALA r0, r31, 0a
opaque:
ADDI64 r254, r254, -16d
LI64 r13, 3d
ST r13, r254, 0a, 8h
LI64 r13, 2d
ST r13, r254, 8a, 8h
LD r1, r254, 0a, 16h
LI64 r3, 3d
ADDI64 r2, r254, 0d
ST r3, r254, 0a, 8h
LI64 r6, 2d
ST r6, r254, 8a, 8h
LD r1, r2, 0a, 16h
ADDI64 r254, r254, 16d
JALA r0, r31, 0a
code size: 299
code size: 323
ret: 0
status: Ok(())

Some files were not shown because too many files have changed in this diff Show more