feat(compiler): list literals, typed compound assignment, file:line diagnostics
- `[a, b, c]` list literals (E_LIST → emit_list); static_type learns slice-element, `new T`, list, string and literal kinds - `x op= y` lowers through the same path as `x = x op y` (emit_bin_vals): fixed `*=`/`/=` use the Q16.16 64-bit paths, string `+=` concatenates, int→long widens; unary `-` keeps a fixed operand's type (arith_ty) - one `unescape()` table for "strings", 'chars' and `interpolation`; `'\''`, `'\\'`, `'\"'` no longer read as 0; unterminated char literals and unexpected characters are errors instead of silently skipped - every diagnostic is `file:line: error: msg` (g_parse_file / g_err_file, Node.file + Node.line set by node()); tok_desc() in expectation errors; duplicate `function` names and unknown `phase` names are reported in source terms (phase_id used to default unknown phases to Overlay) - interpolation holes skip braces inside string literals - hand-IR preludes move from the user `@fn_` prefix to `@lp_` so a user `is_ws` / `str_eq` / `path_join` no longer collides at link time - `@ClearColor(expr)` accepts any constant expression; `Os.pid()` added (docs page + inventory); `str_starts()` in support/str - main.ludic: `else if` flag ladder, char literals, stale script comments - examples/lang/operators.ludic covers all of the above; os.ludic covers Os.pid; docs pages for Os.pid and the Overlay phase; ten changesets - reseeded: selfhost/ludicc.seed.ll is the new compiler's own fixpoint Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
ad548840c7
commit
647dfec334
88 changed files with 30081 additions and 29179 deletions
|
|
@ -233,4 +233,3 @@ function color_lookup(name: pointer) -> int {
|
|||
if (name == "Orchid2") { return 0xAF69EF }
|
||||
return -1
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -45,19 +45,19 @@ function emit_crypto_ns(meth: pointer, e: Node) -> Val {
|
|||
g_uses_cryptort = true
|
||||
if (meth == "sha256") { # SHA-256 -> 64-char hex string
|
||||
let s = emit_expr(e.kids[0])
|
||||
return val(emit_bind(`call ptr @fn_sha256_hex(ptr {s.code})`), "string")
|
||||
return val(emit_bind(`call ptr @lp_sha256_hex(ptr {s.code})`), "string")
|
||||
}
|
||||
if (meth == "hmac_sha256") { # HMAC-SHA256 -> 64-char hex string
|
||||
let k = emit_expr(e.kids[0]); let m = emit_expr(e.kids[1])
|
||||
return val(emit_bind(`call ptr @fn_hmac_sha256_hex(ptr {k.code}, ptr {m.code})`), "string")
|
||||
return val(emit_bind(`call ptr @lp_hmac_sha256_hex(ptr {k.code}, ptr {m.code})`), "string")
|
||||
}
|
||||
if (meth == "hex") { # lowercase hex of a string's bytes
|
||||
let s = emit_expr(e.kids[0])
|
||||
return val(emit_bind(`call ptr @fn_str_hex(ptr {s.code})`), "string")
|
||||
return val(emit_bind(`call ptr @lp_str_hex(ptr {s.code})`), "string")
|
||||
}
|
||||
if (meth == "ct_equal") { # constant-time string equality -> bool
|
||||
let a = emit_expr(e.kids[0]); let b = emit_expr(e.kids[1])
|
||||
return val(emit_bind(`call i32 @fn_ct_streq(ptr {a.code}, ptr {b.code})`), "bool")
|
||||
return val(emit_bind(`call i32 @lp_ct_streq(ptr {a.code}, ptr {b.code})`), "bool")
|
||||
}
|
||||
# random_bytes(n) / random_hex(n): n bytes from the OS CSPRNG, returned as a
|
||||
# 2n-char lowercase hex string. A digest of raw bytes can contain NUL and a
|
||||
|
|
@ -65,21 +65,21 @@ function emit_crypto_ns(meth: pointer, e: Node) -> Val {
|
|||
if (meth == "random_bytes") or (meth == "random_hex") {
|
||||
let n = emit_expr(e.kids[0])
|
||||
let n64 = emit_bind(`sext i32 {n.code} to i64`)
|
||||
return val(emit_bind(`call ptr @fn_random_hex(i64 {n64})`), "string")
|
||||
return val(emit_bind(`call ptr @lp_random_hex(i64 {n64})`), "string")
|
||||
}
|
||||
if (meth == "random_u32") { # one CSPRNG-drawn 32-bit int
|
||||
return val(emit_bind(`call i32 @fn_random_u32()`), "int")
|
||||
return val(emit_bind(`call i32 @lp_random_u32()`), "int")
|
||||
}
|
||||
if (meth == "base64") { # standard base64 (RFC 4648) of a string's bytes
|
||||
let s = emit_expr(e.kids[0])
|
||||
return val(emit_bind(`call ptr @fn_base64(ptr {s.code})`), "string")
|
||||
return val(emit_bind(`call ptr @lp_base64(ptr {s.code})`), "string")
|
||||
}
|
||||
# verify_hmac(key, msg, mac): recompute HMAC-SHA256(key, msg) and compare it to
|
||||
# the supplied hex `mac` in constant time. This is the safe way to check a MAC —
|
||||
# `==` would leak, byte by byte, how much of a forged MAC was correct.
|
||||
let k = emit_expr(e.kids[0]); let m = emit_expr(e.kids[1]); let mac = emit_expr(e.kids[2])
|
||||
let computed = emit_bind(`call ptr @fn_hmac_sha256_hex(ptr {k.code}, ptr {m.code})`)
|
||||
return val(emit_bind(`call i32 @fn_ct_streq(ptr {computed}, ptr {mac.code})`), "bool")
|
||||
let computed = emit_bind(`call ptr @lp_hmac_sha256_hex(ptr {k.code}, ptr {m.code})`)
|
||||
return val(emit_bind(`call i32 @lp_ct_streq(ptr {computed}, ptr {mac.code})`), "bool")
|
||||
}
|
||||
|
||||
# emit_crypto_prelude — the SHA-256 / HMAC-SHA256 runtime, emitted once per program
|
||||
|
|
@ -91,13 +91,13 @@ function emit_crypto_prelude() -> void {
|
|||
emith("@sha256_K = private unnamed_addr constant [64 x i32] [i32 1116352408, i32 1899447441, i32 -1245643825, i32 -373957723, i32 961987163, i32 1508970993, i32 -1841331548, i32 -1424204075, i32 -670586216, i32 310598401, i32 607225278, i32 1426881987, i32 1925078388, i32 -2132889090, i32 -1680079193, i32 -1046744716, i32 -459576895, i32 -272742522, i32 264347078, i32 604807628, i32 770255983, i32 1249150122, i32 1555081692, i32 1996064986, i32 -1740746414, i32 -1473132947, i32 -1341970488, i32 -1084653625, i32 -958395405, i32 -710438585, i32 113926993, i32 338241895, i32 666307205, i32 773529912, i32 1294757372, i32 1396182291, i32 1695183700, i32 1986661051, i32 -2117940946, i32 -1838011259, i32 -1564481375, i32 -1474664885, i32 -1035236496, i32 -949202525, i32 -778901479, i32 -694614492, i32 -200395387, i32 275423344, i32 430227734, i32 506948616, i32 659060556, i32 883997877, i32 958139571, i32 1322822218, i32 1537002063, i32 1747873779, i32 1955562222, i32 2024104815, i32 -2067236844, i32 -1933114872, i32 -1866530822, i32 -1538233109, i32 -1090935817, i32 -965641998]\n")
|
||||
|
||||
# rotate a 32-bit word right by %n (1..31)
|
||||
emith("define i32 @fn_rotr32(i32 %x, i32 %n) {\n")
|
||||
emith("define i32 @lp_rotr32(i32 %x, i32 %n) {\n")
|
||||
emith(" %r = lshr i32 %x, %n\n %m = sub i32 32, %n\n %l = shl i32 %x, %m\n %o = or i32 %r, %l\n ret i32 %o\n}\n")
|
||||
|
||||
# SHA-256 of %len bytes at %msg -> the 32 raw digest bytes at %out. Pads into a
|
||||
# fresh malloc'd buffer (append 0x80, zero-fill, 64-bit big-endian bit length),
|
||||
# then runs the standard 64-round compression over each 512-bit block.
|
||||
emith("define void @fn_sha256_buf(ptr %msg, i64 %len, ptr %out) {\n")
|
||||
emith("define void @lp_sha256_buf(ptr %msg, i64 %len, ptr %out) {\n")
|
||||
emith("entry:\n")
|
||||
emith(" %H = alloca [8 x i32]\n %W = alloca [64 x i32]\n")
|
||||
emith(" %a = alloca i32\n %b = alloca i32\n %c = alloca i32\n %d = alloca i32\n %e = alloca i32\n %f = alloca i32\n %g = alloca i32\n %h = alloca i32\n")
|
||||
|
|
@ -149,10 +149,10 @@ function emit_crypto_prelude() -> void {
|
|||
emith("w2c:\n %xi = load i64, ptr %ip\n %xilt = icmp slt i64 %xi, 64\n br i1 %xilt, label %w2b, label %compinit\n")
|
||||
emith("w2b:\n")
|
||||
emith(" %im15 = sub i64 %xi, 15\n %pm15 = getelementptr [64 x i32], ptr %W, i64 0, i64 %im15\n %w15 = load i32, ptr %pm15\n")
|
||||
emith(" %r7 = call i32 @fn_rotr32(i32 %w15, i32 7)\n %r18 = call i32 @fn_rotr32(i32 %w15, i32 18)\n %sh3 = lshr i32 %w15, 3\n")
|
||||
emith(" %r7 = call i32 @lp_rotr32(i32 %w15, i32 7)\n %r18 = call i32 @lp_rotr32(i32 %w15, i32 18)\n %sh3 = lshr i32 %w15, 3\n")
|
||||
emith(" %x01 = xor i32 %r7, %r18\n %s0 = xor i32 %x01, %sh3\n")
|
||||
emith(" %im2 = sub i64 %xi, 2\n %pm2 = getelementptr [64 x i32], ptr %W, i64 0, i64 %im2\n %w2v = load i32, ptr %pm2\n")
|
||||
emith(" %r17 = call i32 @fn_rotr32(i32 %w2v, i32 17)\n %r19 = call i32 @fn_rotr32(i32 %w2v, i32 19)\n %sh10 = lshr i32 %w2v, 10\n")
|
||||
emith(" %r17 = call i32 @lp_rotr32(i32 %w2v, i32 17)\n %r19 = call i32 @lp_rotr32(i32 %w2v, i32 19)\n %sh10 = lshr i32 %w2v, 10\n")
|
||||
emith(" %x02 = xor i32 %r17, %r19\n %s1 = xor i32 %x02, %sh10\n")
|
||||
emith(" %im16 = sub i64 %xi, 16\n %pm16 = getelementptr [64 x i32], ptr %W, i64 0, i64 %im16\n %w16 = load i32, ptr %pm16\n")
|
||||
emith(" %im7 = sub i64 %xi, 7\n %pm7 = getelementptr [64 x i32], ptr %W, i64 0, i64 %im7\n %w7 = load i32, ptr %pm7\n")
|
||||
|
|
@ -176,7 +176,7 @@ function emit_crypto_prelude() -> void {
|
|||
emith(" %av = load i32, ptr %a\n %bv = load i32, ptr %b\n %cvv = load i32, ptr %c\n %dv = load i32, ptr %d\n")
|
||||
emith(" %ev = load i32, ptr %e\n %fv = load i32, ptr %f\n %gv = load i32, ptr %g\n %hv = load i32, ptr %h\n")
|
||||
# S1 = rotr(e,6) ^ rotr(e,11) ^ rotr(e,25); ch = (e & f) ^ (~e & g)
|
||||
emith(" %e6 = call i32 @fn_rotr32(i32 %ev, i32 6)\n %e11 = call i32 @fn_rotr32(i32 %ev, i32 11)\n %e25 = call i32 @fn_rotr32(i32 %ev, i32 25)\n")
|
||||
emith(" %e6 = call i32 @lp_rotr32(i32 %ev, i32 6)\n %e11 = call i32 @lp_rotr32(i32 %ev, i32 11)\n %e25 = call i32 @lp_rotr32(i32 %ev, i32 25)\n")
|
||||
emith(" %S1a = xor i32 %e6, %e11\n %S1 = xor i32 %S1a, %e25\n")
|
||||
emith(" %ef = and i32 %ev, %fv\n %ne = xor i32 %ev, -1\n %neg = and i32 %ne, %gv\n %ch = xor i32 %ef, %neg\n")
|
||||
emith(" %kp = getelementptr [64 x i32], ptr @sha256_K, i64 0, i64 %ri\n %kv = load i32, ptr %kp\n")
|
||||
|
|
@ -184,7 +184,7 @@ function emit_crypto_prelude() -> void {
|
|||
# temp1 = h + S1 + ch + K[i] + W[i]
|
||||
emith(" %t1a = add i32 %hv, %S1\n %t1b = add i32 %t1a, %ch\n %t1c = add i32 %t1b, %kv\n %temp1 = add i32 %t1c, %wvr\n")
|
||||
# S0 = rotr(a,2) ^ rotr(a,13) ^ rotr(a,22); maj = (a&b) ^ (a&c) ^ (b&c)
|
||||
emith(" %a2r = call i32 @fn_rotr32(i32 %av, i32 2)\n %a13 = call i32 @fn_rotr32(i32 %av, i32 13)\n %a22 = call i32 @fn_rotr32(i32 %av, i32 22)\n")
|
||||
emith(" %a2r = call i32 @lp_rotr32(i32 %av, i32 2)\n %a13 = call i32 @lp_rotr32(i32 %av, i32 13)\n %a22 = call i32 @lp_rotr32(i32 %av, i32 22)\n")
|
||||
emith(" %S0a = xor i32 %a2r, %a13\n %S0 = xor i32 %S0a, %a22\n")
|
||||
emith(" %ab = and i32 %av, %bv\n %ac = and i32 %av, %cvv\n %bc = and i32 %bv, %cvv\n %mj1 = xor i32 %ab, %ac\n %maj = xor i32 %mj1, %bc\n")
|
||||
emith(" %temp2 = add i32 %S0, %maj\n")
|
||||
|
|
@ -218,37 +218,37 @@ function emit_crypto_prelude() -> void {
|
|||
emith("freeb:\n call void @free(ptr %buf)\n ret void\n}\n")
|
||||
|
||||
# one hex digit (0..15) -> its lowercase ASCII byte
|
||||
emith("define i8 @fn_hex_digit(i32 %d) {\n")
|
||||
emith("define i8 @lp_hex_digit(i32 %d) {\n")
|
||||
emith(" %lt = icmp ult i32 %d, 10\n %base = select i1 %lt, i32 48, i32 87\n %v = add i32 %base, %d\n %c = trunc i32 %v to i8\n ret i8 %c\n}\n")
|
||||
|
||||
# hex-encode %n bytes at %in -> a fresh null-terminated 2n-char string
|
||||
emith("define ptr @fn_hex_encode(ptr %in, i64 %n) {\n")
|
||||
emith("define ptr @lp_hex_encode(ptr %in, i64 %n) {\n")
|
||||
emith("entry:\n %ip = alloca i64\n %olen = shl i64 %n, 1\n %olen1 = add i64 %olen, 1\n %s = call ptr @malloc(i64 %olen1)\n store i64 0, ptr %ip\n br label %c\n")
|
||||
emith("c:\n %i = load i64, ptr %ip\n %lt = icmp ult i64 %i, %n\n br i1 %lt, label %bdy, label %done\n")
|
||||
emith("bdy:\n %pp = getelementptr i8, ptr %in, i64 %i\n %byte = load i8, ptr %pp\n %bz = zext i8 %byte to i32\n")
|
||||
emith(" %hi = lshr i32 %bz, 4\n %lo = and i32 %bz, 15\n %hc = call i8 @fn_hex_digit(i32 %hi)\n %lc = call i8 @fn_hex_digit(i32 %lo)\n")
|
||||
emith(" %hi = lshr i32 %bz, 4\n %lo = and i32 %bz, 15\n %hc = call i8 @lp_hex_digit(i32 %hi)\n %lc = call i8 @lp_hex_digit(i32 %lo)\n")
|
||||
emith(" %oi = shl i64 %i, 1\n %o0 = getelementptr i8, ptr %s, i64 %oi\n store i8 %hc, ptr %o0\n %oi1 = add i64 %oi, 1\n %o1 = getelementptr i8, ptr %s, i64 %oi1\n store i8 %lc, ptr %o1\n")
|
||||
emith(" %in1 = add i64 %i, 1\n store i64 %in1, ptr %ip\n br label %c\n")
|
||||
emith("done:\n %tp = getelementptr i8, ptr %s, i64 %olen\n store i8 0, ptr %tp\n ret ptr %s\n}\n")
|
||||
|
||||
# SHA-256 of a null-terminated string -> 64-char hex
|
||||
emith("define ptr @fn_sha256_hex(ptr %s) {\n")
|
||||
emith("define ptr @lp_sha256_hex(ptr %s) {\n")
|
||||
emith("entry:\n %dig = alloca [32 x i8]\n %len = call i64 @strlen(ptr %s)\n %dp = getelementptr [32 x i8], ptr %dig, i64 0, i64 0\n")
|
||||
emith(" call void @fn_sha256_buf(ptr %s, i64 %len, ptr %dp)\n %hex = call ptr @fn_hex_encode(ptr %dp, i64 32)\n ret ptr %hex\n}\n")
|
||||
emith(" call void @lp_sha256_buf(ptr %s, i64 %len, ptr %dp)\n %hex = call ptr @lp_hex_encode(ptr %dp, i64 32)\n ret ptr %hex\n}\n")
|
||||
|
||||
# hex of a whole null-terminated string's bytes
|
||||
emith("define ptr @fn_str_hex(ptr %s) {\n")
|
||||
emith(" %n = call i64 @strlen(ptr %s)\n %h = call ptr @fn_hex_encode(ptr %s, i64 %n)\n ret ptr %h\n}\n")
|
||||
emith("define ptr @lp_str_hex(ptr %s) {\n")
|
||||
emith(" %n = call i64 @strlen(ptr %s)\n %h = call ptr @lp_hex_encode(ptr %s, i64 %n)\n ret ptr %h\n}\n")
|
||||
|
||||
# xor 64 bytes of %src with the byte %pad into %dst (the HMAC key padding step)
|
||||
emith("define void @fn_xor64(ptr %dst, ptr %src, i32 %pad) {\n")
|
||||
emith("define void @lp_xor64(ptr %dst, ptr %src, i32 %pad) {\n")
|
||||
emith("entry:\n %ip = alloca i64\n store i64 0, ptr %ip\n br label %c\n")
|
||||
emith("c:\n %i = load i64, ptr %ip\n %lt = icmp ult i64 %i, 64\n br i1 %lt, label %b, label %d\n")
|
||||
emith("b:\n %sp = getelementptr i8, ptr %src, i64 %i\n %sv = load i8, ptr %sp\n %sz = zext i8 %sv to i32\n %xr = xor i32 %sz, %pad\n %xb = trunc i32 %xr to i8\n %dp = getelementptr i8, ptr %dst, i64 %i\n store i8 %xb, ptr %dp\n %in = add i64 %i, 1\n store i64 %in, ptr %ip\n br label %c\n")
|
||||
emith("d:\n ret void\n}\n")
|
||||
|
||||
# HMAC-SHA256(key, msg) -> 64-char hex (RFC 2104, block size 64).
|
||||
emith("define ptr @fn_hmac_sha256_hex(ptr %key, ptr %msg) {\n")
|
||||
emith("define ptr @lp_hmac_sha256_hex(ptr %key, ptr %msg) {\n")
|
||||
emith("entry:\n")
|
||||
emith(" %k0 = alloca [64 x i8]\n %inner = alloca [32 x i8]\n %outbuf = alloca [96 x i8]\n %fin = alloca [32 x i8]\n")
|
||||
emith(" %klen = call i64 @strlen(ptr %key)\n %mlen = call i64 @strlen(ptr %msg)\n")
|
||||
|
|
@ -256,24 +256,24 @@ function emit_crypto_prelude() -> void {
|
|||
# K0: a key longer than the block is replaced by its own hash; otherwise it is
|
||||
# right-zero-padded to 64 bytes.
|
||||
emith(" %big = icmp ugt i64 %klen, 64\n br i1 %big, label %hashk, label %copyk\n")
|
||||
emith("hashk:\n call void @fn_sha256_buf(ptr %key, i64 %klen, ptr %k0p)\n br label %pads\n")
|
||||
emith("hashk:\n call void @lp_sha256_buf(ptr %key, i64 %klen, ptr %k0p)\n br label %pads\n")
|
||||
emith("copyk:\n call ptr @memcpy(ptr %k0p, ptr %key, i64 %klen)\n br label %pads\n")
|
||||
emith("pads:\n")
|
||||
# inner = SHA-256( (K0 ^ ipad) || msg ), ipad = 0x36
|
||||
emith(" %inlen = add i64 64, %mlen\n %inbuf = call ptr @malloc(i64 %inlen)\n")
|
||||
emith(" call void @fn_xor64(ptr %inbuf, ptr %k0p, i32 54)\n")
|
||||
emith(" call void @lp_xor64(ptr %inbuf, ptr %k0p, i32 54)\n")
|
||||
emith(" %inmsg = getelementptr i8, ptr %inbuf, i64 64\n call ptr @memcpy(ptr %inmsg, ptr %msg, i64 %mlen)\n")
|
||||
emith(" %innerp = getelementptr [32 x i8], ptr %inner, i64 0, i64 0\n call void @fn_sha256_buf(ptr %inbuf, i64 %inlen, ptr %innerp)\n call void @free(ptr %inbuf)\n")
|
||||
emith(" %innerp = getelementptr [32 x i8], ptr %inner, i64 0, i64 0\n call void @lp_sha256_buf(ptr %inbuf, i64 %inlen, ptr %innerp)\n call void @free(ptr %inbuf)\n")
|
||||
# digest = SHA-256( (K0 ^ opad) || inner ), opad = 0x5c
|
||||
emith(" %outp = getelementptr [96 x i8], ptr %outbuf, i64 0, i64 0\n call void @fn_xor64(ptr %outp, ptr %k0p, i32 92)\n")
|
||||
emith(" %outp = getelementptr [96 x i8], ptr %outbuf, i64 0, i64 0\n call void @lp_xor64(ptr %outp, ptr %k0p, i32 92)\n")
|
||||
emith(" %outmsg = getelementptr i8, ptr %outbuf, i64 64\n call ptr @memcpy(ptr %outmsg, ptr %innerp, i64 32)\n")
|
||||
emith(" %finp = getelementptr [32 x i8], ptr %fin, i64 0, i64 0\n call void @fn_sha256_buf(ptr %outp, i64 96, ptr %finp)\n")
|
||||
emith(" %hex = call ptr @fn_hex_encode(ptr %finp, i64 32)\n ret ptr %hex\n}\n")
|
||||
emith(" %finp = getelementptr [32 x i8], ptr %fin, i64 0, i64 0\n call void @lp_sha256_buf(ptr %outp, i64 96, ptr %finp)\n")
|
||||
emith(" %hex = call ptr @lp_hex_encode(ptr %finp, i64 32)\n ret ptr %hex\n}\n")
|
||||
|
||||
# constant-time equality of two null-terminated strings. Length is not secret,
|
||||
# so an unequal length returns early; equal-length inputs are compared with a
|
||||
# data-independent XOR-accumulate that never short-circuits.
|
||||
emith("define i32 @fn_ct_streq(ptr %a, ptr %b) {\n")
|
||||
emith("define i32 @lp_ct_streq(ptr %a, ptr %b) {\n")
|
||||
emith("entry:\n %accp = alloca i32\n %ip = alloca i64\n %la = call i64 @strlen(ptr %a)\n %lb = call i64 @strlen(ptr %b)\n %eqlen = icmp eq i64 %la, %lb\n br i1 %eqlen, label %go, label %ne\n")
|
||||
emith("ne:\n ret i32 0\n")
|
||||
emith("go:\n store i32 0, ptr %accp\n store i64 0, ptr %ip\n br label %c\n")
|
||||
|
|
@ -295,7 +295,7 @@ function emit_secure_rand_prelude() -> void {
|
|||
|
||||
# fill %n bytes at %out from the OS CSPRNG. If /dev/urandom cannot be opened the
|
||||
# buffer is zeroed (documented degraded mode — e.g. wasm), never left uninit.
|
||||
emith("define void @fn_secure_bytes(ptr %out, i64 %n) {\n")
|
||||
emith("define void @lp_secure_bytes(ptr %out, i64 %n) {\n")
|
||||
emith("entry:\n call ptr @memset(ptr %out, i32 0, i64 %n)\n")
|
||||
emith(" %fp = call ptr @fopen(ptr @.ludic_urandom, ptr @.ludic_rbmode)\n")
|
||||
emith(" %isnull = icmp eq ptr %fp, null\n br i1 %isnull, label %fail, label %ok\n")
|
||||
|
|
@ -303,13 +303,13 @@ function emit_secure_rand_prelude() -> void {
|
|||
emith("fail:\n ret void\n}\n")
|
||||
|
||||
# %n secure bytes -> a fresh 2n-char lowercase hex string
|
||||
emith("define ptr @fn_random_hex(i64 %n) {\n")
|
||||
emith("entry:\n %buf = call ptr @malloc(i64 %n)\n call void @fn_secure_bytes(ptr %buf, i64 %n)\n")
|
||||
emith(" %hex = call ptr @fn_hex_encode(ptr %buf, i64 %n)\n call void @free(ptr %buf)\n ret ptr %hex\n}\n")
|
||||
emith("define ptr @lp_random_hex(i64 %n) {\n")
|
||||
emith("entry:\n %buf = call ptr @malloc(i64 %n)\n call void @lp_secure_bytes(ptr %buf, i64 %n)\n")
|
||||
emith(" %hex = call ptr @lp_hex_encode(ptr %buf, i64 %n)\n call void @free(ptr %buf)\n ret ptr %hex\n}\n")
|
||||
|
||||
# one CSPRNG-drawn i32 (little-endian assembly of four secure bytes)
|
||||
emith("define i32 @fn_random_u32() {\n")
|
||||
emith("entry:\n %b = alloca [4 x i8]\n %bp = getelementptr [4 x i8], ptr %b, i64 0, i64 0\n call void @fn_secure_bytes(ptr %bp, i64 4)\n")
|
||||
emith("define i32 @lp_random_u32() {\n")
|
||||
emith("entry:\n %b = alloca [4 x i8]\n %bp = getelementptr [4 x i8], ptr %b, i64 0, i64 0\n call void @lp_secure_bytes(ptr %bp, i64 4)\n")
|
||||
emith(" %p0 = getelementptr i8, ptr %bp, i64 0\n %c0 = load i8, ptr %p0\n %z0 = zext i8 %c0 to i32\n")
|
||||
emith(" %p1 = getelementptr i8, ptr %bp, i64 1\n %c1 = load i8, ptr %p1\n %z1 = zext i8 %c1 to i32\n %s1 = shl i32 %z1, 8\n")
|
||||
emith(" %p2 = getelementptr i8, ptr %bp, i64 2\n %c2 = load i8, ptr %p2\n %z2 = zext i8 %c2 to i32\n %s2 = shl i32 %z2, 16\n")
|
||||
|
|
@ -319,7 +319,7 @@ function emit_secure_rand_prelude() -> void {
|
|||
# standard base64 (RFC 4648, '+' '/' alphabet, '=' padding). The input is copied
|
||||
# into a zero-padded buffer rounded up to a multiple of 3, so the 3-byte group
|
||||
# loop never reads past the string; trailing '=' are written per the remainder.
|
||||
emith("define ptr @fn_base64(ptr %s) {\n")
|
||||
emith("define ptr @lp_base64(ptr %s) {\n")
|
||||
emith("entry:\n %n = call i64 @strlen(ptr %s)\n")
|
||||
emith(" %n2 = add i64 %n, 2\n %grp = udiv i64 %n2, 3\n %bufn = mul i64 %grp, 3\n")
|
||||
emith(" %olen = mul i64 %grp, 4\n %olen1 = add i64 %olen, 1\n %out = call ptr @malloc(i64 %olen1)\n")
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
# (UTC), matching Time.now().
|
||||
# The two civil<->epoch conversions are Howard Hinnant's public-domain algorithms
|
||||
# (chrono-compatible, proleptic Gregorian), emitted once per program as the
|
||||
# @fn_days_from_civil / @fn_civil_from_days prelude and gated by g_uses_datert.
|
||||
# @lp_days_from_civil / @lp_civil_from_days prelude and gated by g_uses_datert.
|
||||
# v1 is UTC-only with no leap seconds; instants are assumed non-negative (dates
|
||||
# at or after 1970). Timezones, format/parse and a game-controlled simulated
|
||||
# clock are tracked follow-ups.
|
||||
|
|
@ -42,7 +42,7 @@ function date_component(ed: pointer, which: int) -> pointer {
|
|||
let yp = emit_alloca("i32")
|
||||
let mp = emit_alloca("i32")
|
||||
let dp = emit_alloca("i32")
|
||||
emit(` call void @fn_civil_from_days(i32 {ed}, ptr {yp}, ptr {mp}, ptr {dp})\n`)
|
||||
emit(` call void @lp_civil_from_days(i32 {ed}, ptr {yp}, ptr {mp}, ptr {dp})\n`)
|
||||
var p = yp
|
||||
if (which == 1) { p = mp }
|
||||
if (which == 2) { p = dp }
|
||||
|
|
@ -60,7 +60,7 @@ function emit_date_ns(meth: pointer, e: Node) -> Val {
|
|||
if (meth == "new") { # new(year, month, day) -> Date (epoch-day)
|
||||
g_uses_datert = true
|
||||
let y = emit_expr(e.kids[0]); let m = emit_expr(e.kids[1]); let d = emit_expr(e.kids[2])
|
||||
return val(emit_bind(`call i32 @fn_days_from_civil(i32 {y.code}, i32 {m.code}, i32 {d.code})`), "int")
|
||||
return val(emit_bind(`call i32 @lp_days_from_civil(i32 {y.code}, i32 {m.code}, i32 {d.code})`), "int")
|
||||
}
|
||||
if (meth == "year") { let ed = emit_expr(e.kids[0]); return val(date_component(ed.code, 0), "int") }
|
||||
if (meth == "month") { let ed = emit_expr(e.kids[0]); return val(date_component(ed.code, 1), "int") }
|
||||
|
|
@ -84,13 +84,13 @@ function emit_date_ns(meth: pointer, e: Node) -> Val {
|
|||
if (meth == "days_in_month") { # length of (year, month) = next month's day 0
|
||||
g_uses_datert = true
|
||||
let y = emit_expr(e.kids[0]); let m = emit_expr(e.kids[1])
|
||||
let this = emit_bind(`call i32 @fn_days_from_civil(i32 {y.code}, i32 {m.code}, i32 1)`)
|
||||
let dec = emit_bind(`icmp eq i32 {m.code}, 12`) # December rolls over to next January
|
||||
let this = emit_bind(`call i32 @lp_days_from_civil(i32 {y.code}, i32 {m.code}, i32 1)`)
|
||||
let dec = emit_bind(`icmp eq i32 {m.code}, 12`) # December rolls over to next January
|
||||
let ny = emit_bind(`add i32 {y.code}, 1`)
|
||||
let ny2 = emit_bind(`select i1 {dec}, i32 {ny}, i32 {y.code}`)
|
||||
let nm = emit_bind(`add i32 {m.code}, 1`)
|
||||
let nm2 = emit_bind(`select i1 {dec}, i32 1, i32 {nm}`)
|
||||
let next = emit_bind(`call i32 @fn_days_from_civil(i32 {ny2}, i32 {nm2}, i32 1)`)
|
||||
let next = emit_bind(`call i32 @lp_days_from_civil(i32 {ny2}, i32 {nm2}, i32 1)`)
|
||||
return val(emit_bind(`sub i32 {next}, {this}`), "int")
|
||||
}
|
||||
if (meth == "to_epoch") { # midnight UTC of the day, as a DateTime instant
|
||||
|
|
@ -132,21 +132,21 @@ function is_datetime_ns(meth: pointer) -> bool {
|
|||
|
||||
# concat two runtime string codes -> a fresh string code
|
||||
function dt_concat(a: pointer, b: pointer) -> pointer {
|
||||
return emit_bind(`call ptr @fn_str_concat(ptr {a}, ptr {b})`)
|
||||
return emit_bind(`call ptr @lp_str_concat(ptr {a}, ptr {b})`)
|
||||
}
|
||||
|
||||
# does the pattern have token `tok` (length tlen) starting at index i?
|
||||
function dt_tok_at(pat: pointer, n: int, i: int, tok: pointer, tlen: int) -> bool {
|
||||
if (i + tlen) > n { return false }
|
||||
var k = 0
|
||||
while k < tlen { if pat[i + k] != tok[k] { return false }; k = k + 1 }
|
||||
while k < tlen { if pat[i + k] != tok[k] { return false }; k += 1 }
|
||||
return true
|
||||
}
|
||||
|
||||
# DateTime.format(t, "pattern") -> string. The pattern MUST be a string literal;
|
||||
# the tokens YYYY / YY / MM / DD / HH / mm / ss expand to zero-padded fields and
|
||||
# every other character is copied through verbatim. Expanded at compile time into
|
||||
# a fold of @fn_str_concat over literal runs and @fn_dt_pad0 field conversions.
|
||||
# a fold of @lp_str_concat over literal runs and @lp_dt_pad0 field conversions.
|
||||
function emit_datetime_format(e: Node) -> Val {
|
||||
g_uses_datert = true
|
||||
g_uses_str = true
|
||||
|
|
@ -157,7 +157,7 @@ function emit_datetime_format(e: Node) -> Val {
|
|||
# compute all six components once
|
||||
let ed = dt_epochday(t.code)
|
||||
let yp = emit_alloca("i32"); let mp = emit_alloca("i32"); let dp = emit_alloca("i32")
|
||||
emit(` call void @fn_civil_from_days(i32 {ed}, ptr {yp}, ptr {mp}, ptr {dp})\n`)
|
||||
emit(` call void @lp_civil_from_days(i32 {ed}, ptr {yp}, ptr {mp}, ptr {dp})\n`)
|
||||
let yv = emit_bind(`load i32, ptr {yp}`)
|
||||
let mv = emit_bind(`load i32, ptr {mp}`)
|
||||
let dv = emit_bind(`load i32, ptr {dp}`)
|
||||
|
|
@ -179,14 +179,14 @@ function emit_datetime_format(e: Node) -> Val {
|
|||
else { if dt_tok_at(pat, n, i, "HH", 2) { field = hh; width = 2; tlen = 2 }
|
||||
else { if dt_tok_at(pat, n, i, "mm", 2) { field = mi; width = 2; tlen = 2 }
|
||||
else { if dt_tok_at(pat, n, i, "ss", 2) { field = ss; width = 2; tlen = 2 } } } } } } }
|
||||
if (field == null) { buf_putc(lit, pat[i]); i = i + 1 }
|
||||
if (field == null) { buf_putc(lit, pat[i]); i += 1 }
|
||||
else {
|
||||
let ls = buf_str(lit)
|
||||
if len(ls) > 0 { acc = dt_concat(acc, emit_str_const(ls)) }
|
||||
let piece = emit_bind(`call ptr @fn_dt_pad0(i32 {field}, i32 {width})`)
|
||||
let piece = emit_bind(`call ptr @lp_dt_pad0(i32 {field}, i32 {width})`)
|
||||
acc = dt_concat(acc, piece)
|
||||
lit.len = 0 # start a fresh literal run
|
||||
i = i + tlen
|
||||
i += tlen
|
||||
}
|
||||
}
|
||||
let tail = buf_str(lit)
|
||||
|
|
@ -223,12 +223,12 @@ function emit_datetime_parse(e: Node) -> Val {
|
|||
else { if dt_tok_at(pat, n, i, "HH", 2) { slot = hp; width = 2; tlen = 2 }
|
||||
else { if dt_tok_at(pat, n, i, "mm", 2) { slot = np; width = 2; tlen = 2 }
|
||||
else { if dt_tok_at(pat, n, i, "ss", 2) { slot = sp; width = 2; tlen = 2 } } } } } }
|
||||
if (slot == null) { off = off + 1; i = i + 1 }
|
||||
if (slot == null) { off += 1; i += 1 }
|
||||
else {
|
||||
let rd = emit_bind(`call i32 @fn_dt_rd(ptr {s.code}, i32 {itoa(off)}, i32 {width}, ptr {failp})`)
|
||||
let rd = emit_bind(`call i32 @lp_dt_rd(ptr {s.code}, i32 {itoa(off)}, i32 {width}, ptr {failp})`)
|
||||
store_at("i32", rd, slot)
|
||||
off = off + width
|
||||
i = i + tlen
|
||||
off += width
|
||||
i += tlen
|
||||
}
|
||||
}
|
||||
let y = emit_bind(`load i32, ptr {yp}`)
|
||||
|
|
@ -237,7 +237,7 @@ function emit_datetime_parse(e: Node) -> Val {
|
|||
let h = emit_bind(`load i32, ptr {hp}`)
|
||||
let mn = emit_bind(`load i32, ptr {np}`)
|
||||
let sc = emit_bind(`load i32, ptr {sp}`)
|
||||
let ed = emit_bind(`call i32 @fn_days_from_civil(i32 {y}, i32 {mo}, i32 {d})`)
|
||||
let ed = emit_bind(`call i32 @lp_days_from_civil(i32 {y}, i32 {mo}, i32 {d})`)
|
||||
let days = emit_bind(`mul i32 {ed}, 86400`)
|
||||
let hs = emit_bind(`mul i32 {h}, 3600`)
|
||||
let ns = emit_bind(`mul i32 {mn}, 60`)
|
||||
|
|
@ -256,7 +256,7 @@ function emit_datetime_ns(meth: pointer, e: Node) -> Val {
|
|||
g_uses_datert = true
|
||||
let y = emit_expr(e.kids[0]); let mo = emit_expr(e.kids[1]); let d = emit_expr(e.kids[2])
|
||||
let h = emit_expr(e.kids[3]); let mi = emit_expr(e.kids[4]); let s = emit_expr(e.kids[5])
|
||||
let ed = emit_bind(`call i32 @fn_days_from_civil(i32 {y.code}, i32 {mo.code}, i32 {d.code})`)
|
||||
let ed = emit_bind(`call i32 @lp_days_from_civil(i32 {y.code}, i32 {mo.code}, i32 {d.code})`)
|
||||
let days = emit_bind(`mul i32 {ed}, 86400`)
|
||||
let hh = emit_bind(`mul i32 {h.code}, 3600`)
|
||||
let mm = emit_bind(`mul i32 {mi.code}, 60`)
|
||||
|
|
@ -306,8 +306,8 @@ function emit_datetime_ns(meth: pointer, e: Node) -> Val {
|
|||
# are deterministic and bit-identical everywhere. 719468 is the day count from
|
||||
# 0000-03-01 to 1970-01-01; 146097 is the days in a 400-year era.
|
||||
function emit_datetime_prelude() -> void {
|
||||
# @fn_days_from_civil(y, m, d) -> days since 1970-01-01
|
||||
emith("define i32 @fn_days_from_civil(i32 %y0, i32 %m, i32 %d) {\n")
|
||||
# @lp_days_from_civil(y, m, d) -> days since 1970-01-01
|
||||
emith("define i32 @lp_days_from_civil(i32 %y0, i32 %m, i32 %d) {\n")
|
||||
emith(" %mle2 = icmp sle i32 %m, 2\n")
|
||||
emith(" %ysub = select i1 %mle2, i32 1, i32 0\n")
|
||||
emith(" %y = sub i32 %y0, %ysub\n")
|
||||
|
|
@ -336,9 +336,9 @@ function emit_datetime_prelude() -> void {
|
|||
emith(" %r = sub i32 %r0, 719468\n")
|
||||
emith(" ret i32 %r\n")
|
||||
emith("}\n")
|
||||
# @fn_civil_from_days(z0, yp, mp, dp): write the civil (year, month, day) of
|
||||
# @lp_civil_from_days(z0, yp, mp, dp): write the civil (year, month, day) of
|
||||
# the epoch-day z0 through the three out-pointers.
|
||||
emith("define void @fn_civil_from_days(i32 %z0, ptr %yp, ptr %mp, ptr %dp) {\n")
|
||||
emith("define void @lp_civil_from_days(i32 %z0, ptr %yp, ptr %mp, ptr %dp) {\n")
|
||||
emith(" %z = add i32 %z0, 719468\n")
|
||||
emith(" %zneg = icmp slt i32 %z, 0\n")
|
||||
emith(" %zm = sub i32 %z, 146096\n")
|
||||
|
|
@ -381,9 +381,9 @@ function emit_datetime_prelude() -> void {
|
|||
emith(" store i32 %dd, ptr %dp\n")
|
||||
emith(" ret void\n")
|
||||
emith("}\n")
|
||||
# @fn_dt_pad0(v, w): a fresh, malloc'd, w-digit zero-padded decimal of v
|
||||
# @lp_dt_pad0(v, w): a fresh, malloc'd, w-digit zero-padded decimal of v
|
||||
# (v assumed non-negative). Backs DateTime.format's numeric fields.
|
||||
emith("define ptr @fn_dt_pad0(i32 %v, i32 %w) {\n")
|
||||
emith("define ptr @lp_dt_pad0(i32 %v, i32 %w) {\n")
|
||||
emith(" %we = zext i32 %w to i64\n")
|
||||
emith(" %sz = add i64 %we, 1\n")
|
||||
emith(" %buf = call ptr @malloc(i64 %sz)\n")
|
||||
|
|
@ -397,9 +397,9 @@ function emit_datetime_prelude() -> void {
|
|||
emith(" %cp = getelementptr i8, ptr %buf, i32 %k\n store i8 %ch8, ptr %cp\n")
|
||||
emith(" %vn = sdiv i32 %vv, 10\n store i32 %vn, ptr %vp\n %kn = sub i32 %k, 1\n store i32 %kn, ptr %kp\n br label %loop\n")
|
||||
emith("done:\n ret ptr %buf\n}\n")
|
||||
# @fn_dt_rd(s, off, w, failp): read w decimal digits of s starting at off into an
|
||||
# @lp_dt_rd(s, off, w, failp): read w decimal digits of s starting at off into an
|
||||
# int; on any non-digit set *failp = 1. Backs DateTime.parse's fixed-width fields.
|
||||
emith("define i32 @fn_dt_rd(ptr %s, i32 %off, i32 %w, ptr %failp) {\n")
|
||||
emith("define i32 @lp_dt_rd(ptr %s, i32 %off, i32 %w, ptr %failp) {\n")
|
||||
emith(" %accp = alloca i32\n store i32 0, ptr %accp\n %kp = alloca i32\n store i32 0, ptr %kp\n")
|
||||
emith(" br label %loop\n")
|
||||
emith("loop:\n %k = load i32, ptr %kp\n %kok = icmp slt i32 %k, %w\n br i1 %kok, label %body, label %done\n")
|
||||
|
|
|
|||
|
|
@ -65,10 +65,10 @@ function ease_eval(mode: int, t: pointer) -> pointer {
|
|||
g_uses_mathrt = true # 2^(-10t) * sin((10t - 0.75) * 2pi/3) + 1
|
||||
let tt = emit_bind(`mul i32 {t}, 10`) # 10t
|
||||
let ntt = emit_bind(`sub i32 0, {tt}`) # -10t (exp2 exponent, Q16.16)
|
||||
let decay = emit_bind(`call i32 @fn_fx_exp2(i32 {ntt})`)
|
||||
let decay = emit_bind(`call i32 @lp_fx_exp2(i32 {ntt})`)
|
||||
let ph = emit_bind(`sub i32 {tt}, 49152`) # 10t - 0.75
|
||||
let ang = fx_mul_code(ph, "137258") # * (2pi/3), 2pi/3 = 137258 fixed
|
||||
let s = emit_bind(`call i32 @fn_fx_sin(i32 {ang})`)
|
||||
let s = emit_bind(`call i32 @lp_fx_sin(i32 {ang})`)
|
||||
let osc = fx_mul_code(decay, s)
|
||||
return emit_bind(`add i32 {osc}, 65536`)
|
||||
}
|
||||
|
|
@ -82,6 +82,6 @@ function emit_ease_ns(meth: pointer, e: Node) -> Val {
|
|||
if (meth == "out") { return val(ease_eval(2, t.code), "fixed") }
|
||||
if (meth == "in_out") { return val(ease_eval(3, t.code), "fixed") }
|
||||
if (meth == "back") { return val(ease_eval(4, t.code), "fixed") }
|
||||
if (meth == "elastic"){ return val(ease_eval(5, t.code), "fixed") }
|
||||
if (meth == "elastic") { return val(ease_eval(5, t.code), "fixed") }
|
||||
return val(ease_eval(6, t.code), "fixed") # bounce
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,38 +54,38 @@ function is_mime_ns(meth: pointer) -> bool {
|
|||
function emit_fs_ns(meth: pointer, e: Node) -> Val {
|
||||
g_uses_fsrt = true
|
||||
let a = emit_expr(e.kids[0])
|
||||
if (meth == "exists") { return val(emit_bind(`call i32 @fn_fs_exists(ptr {a.code})`), "bool") }
|
||||
if (meth == "is_dir") { return val(emit_bind(`call i32 @fn_fs_is_dir(ptr {a.code})`), "bool") }
|
||||
if (meth == "read_text") { return val(emit_bind(`call ptr @fn_fs_read_text(ptr {a.code})`), "string") }
|
||||
if (meth == "remove") { return val(emit_bind(`call i32 @fn_fs_remove(ptr {a.code})`), "bool") }
|
||||
if (meth == "size") { return val(emit_bind(`call i32 @fn_fs_size(ptr {a.code})`), "int") }
|
||||
if (meth == "mkdir") { return val(emit_bind(`call i32 @fn_fs_mkdir(ptr {a.code})`), "bool") }
|
||||
if (meth == "list") { return val(emit_bind(`call ptr @fn_fs_list(ptr {a.code})`), "[]string") }
|
||||
if (meth == "exists") { return val(emit_bind(`call i32 @lp_fs_exists(ptr {a.code})`), "bool") }
|
||||
if (meth == "is_dir") { return val(emit_bind(`call i32 @lp_fs_is_dir(ptr {a.code})`), "bool") }
|
||||
if (meth == "read_text") { return val(emit_bind(`call ptr @lp_fs_read_text(ptr {a.code})`), "string") }
|
||||
if (meth == "remove") { return val(emit_bind(`call i32 @lp_fs_remove(ptr {a.code})`), "bool") }
|
||||
if (meth == "size") { return val(emit_bind(`call i32 @lp_fs_size(ptr {a.code})`), "int") }
|
||||
if (meth == "mkdir") { return val(emit_bind(`call i32 @lp_fs_mkdir(ptr {a.code})`), "bool") }
|
||||
if (meth == "list") { return val(emit_bind(`call ptr @lp_fs_list(ptr {a.code})`), "[]string") }
|
||||
let b = emit_expr(e.kids[1])
|
||||
if (meth == "write_text") { return val(emit_bind(`call i32 @fn_fs_write_text(ptr {a.code}, ptr {b.code})`), "bool") }
|
||||
if (meth == "append_text") { return val(emit_bind(`call i32 @fn_fs_append_text(ptr {a.code}, ptr {b.code})`), "bool") }
|
||||
if (meth == "write_text") { return val(emit_bind(`call i32 @lp_fs_write_text(ptr {a.code}, ptr {b.code})`), "bool") }
|
||||
if (meth == "append_text") { return val(emit_bind(`call i32 @lp_fs_append_text(ptr {a.code}, ptr {b.code})`), "bool") }
|
||||
# copy
|
||||
return val(emit_bind(`call i32 @fn_fs_copy(ptr {a.code}, ptr {b.code})`), "bool")
|
||||
return val(emit_bind(`call i32 @lp_fs_copy(ptr {a.code}, ptr {b.code})`), "bool")
|
||||
}
|
||||
|
||||
function emit_path_ns(meth: pointer, e: Node) -> Val {
|
||||
g_uses_fsrt = true
|
||||
let a = emit_expr(e.kids[0])
|
||||
if (meth == "dir") { return val(emit_bind(`call ptr @fn_path_dir(ptr {a.code})`), "string") }
|
||||
if (meth == "base") { return val(emit_bind(`call ptr @fn_path_base(ptr {a.code})`), "string") }
|
||||
if (meth == "ext") { return val(emit_bind(`call ptr @fn_path_ext(ptr {a.code})`), "string") }
|
||||
if (meth == "stem") { return val(emit_bind(`call ptr @fn_path_stem(ptr {a.code})`), "string") }
|
||||
if (meth == "normalize") { return val(emit_bind(`call ptr @fn_path_norm(ptr {a.code})`), "string") }
|
||||
if (meth == "dir") { return val(emit_bind(`call ptr @lp_path_dir(ptr {a.code})`), "string") }
|
||||
if (meth == "base") { return val(emit_bind(`call ptr @lp_path_base(ptr {a.code})`), "string") }
|
||||
if (meth == "ext") { return val(emit_bind(`call ptr @lp_path_ext(ptr {a.code})`), "string") }
|
||||
if (meth == "stem") { return val(emit_bind(`call ptr @lp_path_stem(ptr {a.code})`), "string") }
|
||||
if (meth == "normalize") { return val(emit_bind(`call ptr @lp_path_norm(ptr {a.code})`), "string") }
|
||||
# join
|
||||
let b = emit_expr(e.kids[1])
|
||||
return val(emit_bind(`call ptr @fn_path_join(ptr {a.code}, ptr {b.code})`), "string")
|
||||
return val(emit_bind(`call ptr @lp_path_join(ptr {a.code}, ptr {b.code})`), "string")
|
||||
}
|
||||
|
||||
function emit_mime_ns(meth: pointer, e: Node) -> Val {
|
||||
g_uses_fsrt = true
|
||||
let a = emit_expr(e.kids[0])
|
||||
if (meth == "sniff") { return val(emit_bind(`call ptr @fn_mime_sniff(ptr {a.code})`), "string") }
|
||||
return val(emit_bind(`call ptr @fn_mime_of(ptr {a.code})`), "string")
|
||||
if (meth == "sniff") { return val(emit_bind(`call ptr @lp_mime_sniff(ptr {a.code})`), "string") }
|
||||
return val(emit_bind(`call ptr @lp_mime_of(ptr {a.code})`), "string")
|
||||
}
|
||||
|
||||
# emit_fs_prelude — the Fs/Path/Mime runtime, emitted once per program that uses
|
||||
|
|
@ -108,15 +108,15 @@ function emit_fs_prelude() -> void {
|
|||
# ---- shared helpers --------------------------------------------------------
|
||||
function emit_fs_common() -> void {
|
||||
# duplicate %n bytes of %s into a fresh NUL-terminated buffer
|
||||
emith("define ptr @fn_fs_dup(ptr %s, i32 %n) {\n")
|
||||
emith("define ptr @lp_fs_dup(ptr %s, i32 %n) {\n")
|
||||
emith("entry:\n %nz = zext i32 %n to i64\n %t = add i64 %nz, 1\n %m = call ptr @malloc(i64 %t)\n call ptr @memcpy(ptr %m, ptr %s, i64 %nz)\n")
|
||||
emith(" %end = getelementptr i8, ptr %m, i32 %n\n store i8 0, ptr %end\n ret ptr %m\n}\n")
|
||||
|
||||
emith("define ptr @fn_fs_strdup(ptr %s) {\n")
|
||||
emith("entry:\n %l = call i64 @strlen(ptr %s)\n %li = trunc i64 %l to i32\n %r = call ptr @fn_fs_dup(ptr %s, i32 %li)\n ret ptr %r\n}\n")
|
||||
emith("define ptr @lp_fs_strdup(ptr %s) {\n")
|
||||
emith("entry:\n %l = call i64 @strlen(ptr %s)\n %li = trunc i64 %l to i32\n %r = call ptr @lp_fs_dup(ptr %s, i32 %li)\n ret ptr %r\n}\n")
|
||||
|
||||
# index of the last '/' in %s, or -1 if none
|
||||
emith("define i32 @fn_fs_lastslash(ptr %s) {\n")
|
||||
emith("define i32 @lp_fs_lastslash(ptr %s) {\n")
|
||||
emith("entry:\n %ip = alloca i32\n %rp = alloca i32\n store i32 0, ptr %ip\n store i32 -1, ptr %rp\n br label %lp\n")
|
||||
emith("lp:\n %i = load i32, ptr %ip\n %p = getelementptr i8, ptr %s, i32 %i\n %b = load i8, ptr %p\n %c = zext i8 %b to i32\n")
|
||||
emith(" %z = icmp eq i32 %c, 0\n br i1 %z, label %done, label %go\n")
|
||||
|
|
@ -130,14 +130,14 @@ function emit_fs_common() -> void {
|
|||
function emit_fs_path() -> void {
|
||||
# join(a, b): b absolute -> b; a empty -> b; b empty -> a; else a + "/" + b
|
||||
# (avoiding a doubled separator when a already ends with one).
|
||||
emith("define ptr @fn_path_join(ptr %a, ptr %b) {\n")
|
||||
emith("define ptr @lp_path_join(ptr %a, ptr %b) {\n")
|
||||
emith("entry:\n %la64 = call i64 @strlen(ptr %a)\n %la = trunc i64 %la64 to i32\n %lb64 = call i64 @strlen(ptr %b)\n %lb = trunc i64 %lb64 to i32\n")
|
||||
emith(" %b0 = load i8, ptr %b\n %b0i = zext i8 %b0 to i32\n %babs = icmp eq i32 %b0i, 47\n br i1 %babs, label %retb, label %ka\n")
|
||||
emith("retb:\n %rb = call ptr @fn_fs_strdup(ptr %b)\n ret ptr %rb\n")
|
||||
emith("retb:\n %rb = call ptr @lp_fs_strdup(ptr %b)\n ret ptr %rb\n")
|
||||
emith("ka:\n %aemp = icmp eq i32 %la, 0\n br i1 %aemp, label %retb2, label %kb\n")
|
||||
emith("retb2:\n %rb2 = call ptr @fn_fs_strdup(ptr %b)\n ret ptr %rb2\n")
|
||||
emith("retb2:\n %rb2 = call ptr @lp_fs_strdup(ptr %b)\n ret ptr %rb2\n")
|
||||
emith("kb:\n %bemp = icmp eq i32 %lb, 0\n br i1 %bemp, label %reta, label %chk\n")
|
||||
emith("reta:\n %ra = call ptr @fn_fs_strdup(ptr %a)\n ret ptr %ra\n")
|
||||
emith("reta:\n %ra = call ptr @lp_fs_strdup(ptr %a)\n ret ptr %ra\n")
|
||||
emith("chk:\n %lam1 = sub i32 %la, 1\n %pe = getelementptr i8, ptr %a, i32 %lam1\n %ae = load i8, ptr %pe\n %aei = zext i8 %ae to i32\n %ends = icmp eq i32 %aei, 47\n")
|
||||
emith(" %sep = select i1 %ends, i32 0, i32 1\n %tot = add i32 %la, %lb\n %tot2 = add i32 %tot, %sep\n %tot3 = add i32 %tot2, 1\n %totz = zext i32 %tot3 to i64\n %m = call ptr @malloc(i64 %totz)\n")
|
||||
emith(" %laz = zext i32 %la to i64\n call ptr @memcpy(ptr %m, ptr %a, i64 %laz)\n")
|
||||
|
|
@ -149,39 +149,39 @@ function emit_fs_path() -> void {
|
|||
emith(" %endoff = add i32 %boff, %lb\n %ep = getelementptr i8, ptr %m, i32 %endoff\n store i8 0, ptr %ep\n ret ptr %m\n}\n")
|
||||
|
||||
# base(p): the component after the last '/', or p itself
|
||||
emith("define ptr @fn_path_base(ptr %s) {\n")
|
||||
emith("entry:\n %ls = call i32 @fn_fs_lastslash(ptr %s)\n %none = icmp eq i32 %ls, -1\n br i1 %none, label %whole, label %tail\n")
|
||||
emith("whole:\n %r = call ptr @fn_fs_strdup(ptr %s)\n ret ptr %r\n")
|
||||
emith("tail:\n %st = add i32 %ls, 1\n %p = getelementptr i8, ptr %s, i32 %st\n %r2 = call ptr @fn_fs_strdup(ptr %p)\n ret ptr %r2\n}\n")
|
||||
emith("define ptr @lp_path_base(ptr %s) {\n")
|
||||
emith("entry:\n %ls = call i32 @lp_fs_lastslash(ptr %s)\n %none = icmp eq i32 %ls, -1\n br i1 %none, label %whole, label %tail\n")
|
||||
emith("whole:\n %r = call ptr @lp_fs_strdup(ptr %s)\n ret ptr %r\n")
|
||||
emith("tail:\n %st = add i32 %ls, 1\n %p = getelementptr i8, ptr %s, i32 %st\n %r2 = call ptr @lp_fs_strdup(ptr %p)\n ret ptr %r2\n}\n")
|
||||
|
||||
# dir(p): everything before the last '/', or "." if none; "/" stays "/"
|
||||
emith("define ptr @fn_path_dir(ptr %s) {\n")
|
||||
emith("entry:\n %ls = call i32 @fn_fs_lastslash(ptr %s)\n %none = icmp eq i32 %ls, -1\n br i1 %none, label %dot, label %chk0\n")
|
||||
emith("dot:\n %d = call ptr @fn_fs_strdup(ptr @fn_str_dot)\n ret ptr %d\n")
|
||||
emith("define ptr @lp_path_dir(ptr %s) {\n")
|
||||
emith("entry:\n %ls = call i32 @lp_fs_lastslash(ptr %s)\n %none = icmp eq i32 %ls, -1\n br i1 %none, label %dot, label %chk0\n")
|
||||
emith("dot:\n %d = call ptr @lp_fs_strdup(ptr @fn_str_dot)\n ret ptr %d\n")
|
||||
emith("chk0:\n %isroot = icmp eq i32 %ls, 0\n br i1 %isroot, label %root, label %cut\n")
|
||||
emith("root:\n %r = call ptr @fn_fs_strdup(ptr @fn_str_slash)\n ret ptr %r\n")
|
||||
emith("cut:\n %r2 = call ptr @fn_fs_dup(ptr %s, i32 %ls)\n ret ptr %r2\n}\n")
|
||||
emith("root:\n %r = call ptr @lp_fs_strdup(ptr @fn_str_slash)\n ret ptr %r\n")
|
||||
emith("cut:\n %r2 = call ptr @lp_fs_dup(ptr %s, i32 %ls)\n ret ptr %r2\n}\n")
|
||||
|
||||
# ext(p): from the last '.' in the base component to the end, incl. the dot;
|
||||
# "" when the base has no '.' or begins with '.' (a dotfile has no extension)
|
||||
emith("define ptr @fn_path_ext(ptr %s) {\n")
|
||||
emith("entry:\n %ls = call i32 @fn_fs_lastslash(ptr %s)\n %bstart = add i32 %ls, 1\n") # ls=-1 -> bstart=0
|
||||
emith("define ptr @lp_path_ext(ptr %s) {\n")
|
||||
emith("entry:\n %ls = call i32 @lp_fs_lastslash(ptr %s)\n %bstart = add i32 %ls, 1\n") # ls=-1 -> bstart=0
|
||||
emith(" %dp = alloca i32\n %ip = alloca i32\n store i32 -1, ptr %dp\n store i32 %bstart, ptr %ip\n br label %lp\n")
|
||||
emith("lp:\n %i = load i32, ptr %ip\n %p = getelementptr i8, ptr %s, i32 %i\n %b = load i8, ptr %p\n %c = zext i8 %b to i32\n %z = icmp eq i32 %c, 0\n br i1 %z, label %done, label %go\n")
|
||||
emith("go:\n %dot = icmp eq i32 %c, 46\n br i1 %dot, label %sd, label %nx\n")
|
||||
emith("sd:\n store i32 %i, ptr %dp\n br label %nx\n")
|
||||
emith("nx:\n %i1 = add i32 %i, 1\n store i32 %i1, ptr %ip\n br label %lp\n")
|
||||
emith("done:\n %d = load i32, ptr %dp\n %nod = icmp eq i32 %d, -1\n br i1 %nod, label %empty, label %chkpos\n")
|
||||
emith("empty:\n %e = call ptr @fn_fs_strdup(ptr @fn_str_empty)\n ret ptr %e\n")
|
||||
emith("empty:\n %e = call ptr @lp_fs_strdup(ptr @fn_str_empty)\n ret ptr %e\n")
|
||||
emith("chkpos:\n %atstart = icmp eq i32 %d, %bstart\n br i1 %atstart, label %empty2, label %take\n")
|
||||
emith("empty2:\n %e2 = call ptr @fn_fs_strdup(ptr @fn_str_empty)\n ret ptr %e2\n")
|
||||
emith("take:\n %pp = getelementptr i8, ptr %s, i32 %d\n %r = call ptr @fn_fs_strdup(ptr %pp)\n ret ptr %r\n}\n")
|
||||
emith("empty2:\n %e2 = call ptr @lp_fs_strdup(ptr @fn_str_empty)\n ret ptr %e2\n")
|
||||
emith("take:\n %pp = getelementptr i8, ptr %s, i32 %d\n %r = call ptr @lp_fs_strdup(ptr %pp)\n ret ptr %r\n}\n")
|
||||
|
||||
# stem(p): the base component without its extension
|
||||
emith("define ptr @fn_path_stem(ptr %s) {\n")
|
||||
emith("entry:\n %base = call ptr @fn_path_base(ptr %s)\n %ext = call ptr @fn_path_ext(ptr %s)\n")
|
||||
emith("define ptr @lp_path_stem(ptr %s) {\n")
|
||||
emith("entry:\n %base = call ptr @lp_path_base(ptr %s)\n %ext = call ptr @lp_path_ext(ptr %s)\n")
|
||||
emith(" %bl64 = call i64 @strlen(ptr %base)\n %bl = trunc i64 %bl64 to i32\n %el64 = call i64 @strlen(ptr %ext)\n %el = trunc i64 %el64 to i32\n")
|
||||
emith(" %keep = sub i32 %bl, %el\n %r = call ptr @fn_fs_dup(ptr %base, i32 %keep)\n ret ptr %r\n}\n")
|
||||
emith(" %keep = sub i32 %bl, %el\n %r = call ptr @lp_fs_dup(ptr %base, i32 %keep)\n ret ptr %r\n}\n")
|
||||
|
||||
emit_fs_normalize()
|
||||
}
|
||||
|
|
@ -190,7 +190,7 @@ function emit_fs_path() -> void {
|
|||
# popping the previous kept segment (never above an absolute root). Preserves a
|
||||
# leading '/'. An empty result becomes ".".
|
||||
function emit_fs_normalize() -> void {
|
||||
emith("define ptr @fn_path_norm(ptr %s) {\n")
|
||||
emith("define ptr @lp_path_norm(ptr %s) {\n")
|
||||
emith("entry:\n %len64 = call i64 @strlen(ptr %s)\n %len = trunc i64 %len64 to i32\n %cap = add i32 %len, 2\n %capz = zext i32 %cap to i64\n")
|
||||
emith(" %out = call ptr @malloc(i64 %capz)\n")
|
||||
# segst holds output offsets (i32) of each kept segment's start; size cap ints
|
||||
|
|
@ -240,17 +240,17 @@ function emit_fs_normalize() -> void {
|
|||
|
||||
# ---- Fs.* (libc) -----------------------------------------------------------
|
||||
function emit_fs_io() -> void {
|
||||
emith("define i32 @fn_fs_exists(ptr %p) {\n")
|
||||
emith("define i32 @lp_fs_exists(ptr %p) {\n")
|
||||
emith("entry:\n %r = call i32 @access(ptr %p, i32 0)\n %ok = icmp eq i32 %r, 0\n %z = zext i1 %ok to i32\n ret i32 %z\n}\n")
|
||||
|
||||
# is_dir: opendir succeeds iff it is a directory
|
||||
emith("define i32 @fn_fs_is_dir(ptr %p) {\n")
|
||||
emith("define i32 @lp_fs_is_dir(ptr %p) {\n")
|
||||
emith("entry:\n %d = call ptr @opendir(ptr %p)\n %nz = icmp ne ptr %d, null\n br i1 %nz, label %yes, label %no\n")
|
||||
emith("yes:\n call i32 @closedir(ptr %d)\n ret i32 1\n")
|
||||
emith("no:\n ret i32 0\n}\n")
|
||||
|
||||
# size: bytes via fseek/ftell, or -1 if it cannot be opened
|
||||
emith("define i32 @fn_fs_size(ptr %p) {\n")
|
||||
emith("define i32 @lp_fs_size(ptr %p) {\n")
|
||||
emith("entry:\n %f = call ptr @fopen(ptr %p, ptr @fn_str_rb)\n %nz = icmp ne ptr %f, null\n br i1 %nz, label %ok, label %bad\n")
|
||||
emith("bad:\n ret i32 -1\n")
|
||||
emith("ok:\n call i32 @fseek(ptr %f, i64 0, i32 2)\n %n = call i64 @ftell(ptr %f)\n call i32 @fclose(ptr %f)\n %ni = trunc i64 %n to i32\n ret i32 %ni\n}\n")
|
||||
|
|
@ -258,54 +258,54 @@ function emit_fs_io() -> void {
|
|||
# readall: whole file into a fresh buffer; store byte length to %lenout; null on
|
||||
# failure. The buffer is NUL-terminated (one past the length) so text callers
|
||||
# can use it directly while binary callers use the length.
|
||||
emith("define ptr @fn_fs_readall(ptr %p, ptr %lenout) {\n")
|
||||
emith("define ptr @lp_fs_readall(ptr %p, ptr %lenout) {\n")
|
||||
emith("entry:\n store i32 0, ptr %lenout\n %f = call ptr @fopen(ptr %p, ptr @fn_str_rb)\n %nz = icmp ne ptr %f, null\n br i1 %nz, label %ok, label %bad\n")
|
||||
emith("bad:\n ret ptr null\n")
|
||||
emith("ok:\n call i32 @fseek(ptr %f, i64 0, i32 2)\n %n64 = call i64 @ftell(ptr %f)\n call i32 @fseek(ptr %f, i64 0, i32 0)\n %n = trunc i64 %n64 to i32\n")
|
||||
emith(" %cap = add i64 %n64, 1\n %m = call ptr @malloc(i64 %cap)\n %rd = call i64 @fread(ptr %m, i64 1, i64 %n64, ptr %f)\n call i32 @fclose(ptr %f)\n")
|
||||
emith(" %rdi = trunc i64 %rd to i32\n %endp = getelementptr i8, ptr %m, i32 %rdi\n store i8 0, ptr %endp\n store i32 %rdi, ptr %lenout\n ret ptr %m\n}\n")
|
||||
|
||||
emith("define ptr @fn_fs_read_text(ptr %p) {\n")
|
||||
emith("entry:\n %lp = alloca i32\n %r = call ptr @fn_fs_readall(ptr %p, ptr %lp)\n ret ptr %r\n}\n")
|
||||
emith("define ptr @lp_fs_read_text(ptr %p) {\n")
|
||||
emith("entry:\n %lp = alloca i32\n %r = call ptr @lp_fs_readall(ptr %p, ptr %lp)\n ret ptr %r\n}\n")
|
||||
|
||||
# write_text: write to "<p>.tmp" then rename over %p, so a crash mid-write
|
||||
# never truncates the previous file. Returns 1 on success.
|
||||
emith("define i32 @fn_fs_write_text(ptr %p, ptr %s) {\n")
|
||||
emith("entry:\n %tmp = call ptr @fn_path_join_ext(ptr %p, ptr @fn_str_dottmp)\n %f = call ptr @fopen(ptr %tmp, ptr @fn_str_wb)\n %nz = icmp ne ptr %f, null\n br i1 %nz, label %ok, label %bad\n")
|
||||
emith("define i32 @lp_fs_write_text(ptr %p, ptr %s) {\n")
|
||||
emith("entry:\n %tmp = call ptr @lp_path_join_ext(ptr %p, ptr @fn_str_dottmp)\n %f = call ptr @fopen(ptr %tmp, ptr @fn_str_wb)\n %nz = icmp ne ptr %f, null\n br i1 %nz, label %ok, label %bad\n")
|
||||
emith("bad:\n ret i32 0\n")
|
||||
emith("ok:\n %n = call i64 @strlen(ptr %s)\n %w = call i64 @fwrite(ptr %s, i64 1, i64 %n, ptr %f)\n call i32 @fclose(ptr %f)\n")
|
||||
emith(" %rr = call i32 @rename(ptr %tmp, ptr %p)\n %ok2 = icmp eq i32 %rr, 0\n %z = zext i1 %ok2 to i32\n ret i32 %z\n}\n")
|
||||
|
||||
# concat two strings (used to build "<p>.tmp"); local so write_text needs no
|
||||
# dependency on the Os prelude
|
||||
emith("define ptr @fn_path_join_ext(ptr %a, ptr %b) {\n")
|
||||
emith("define ptr @lp_path_join_ext(ptr %a, ptr %b) {\n")
|
||||
emith("entry:\n %la = call i64 @strlen(ptr %a)\n %lb = call i64 @strlen(ptr %b)\n %sum = add i64 %la, %lb\n %tot = add i64 %sum, 1\n %m = call ptr @malloc(i64 %tot)\n")
|
||||
emith(" call ptr @memcpy(ptr %m, ptr %a, i64 %la)\n %m2 = getelementptr i8, ptr %m, i64 %la\n call ptr @memcpy(ptr %m2, ptr %b, i64 %lb)\n %ep = getelementptr i8, ptr %m, i64 %sum\n store i8 0, ptr %ep\n ret ptr %m\n}\n")
|
||||
|
||||
emith("define i32 @fn_fs_append_text(ptr %p, ptr %s) {\n")
|
||||
emith("define i32 @lp_fs_append_text(ptr %p, ptr %s) {\n")
|
||||
emith("entry:\n %f = call ptr @fopen(ptr %p, ptr @fn_str_ab)\n %nz = icmp ne ptr %f, null\n br i1 %nz, label %ok, label %bad\n")
|
||||
emith("bad:\n ret i32 0\n")
|
||||
emith("ok:\n %n = call i64 @strlen(ptr %s)\n call i64 @fwrite(ptr %s, i64 1, i64 %n, ptr %f)\n call i32 @fclose(ptr %f)\n ret i32 1\n}\n")
|
||||
|
||||
emith("define i32 @fn_fs_remove(ptr %p) {\n")
|
||||
emith("define i32 @lp_fs_remove(ptr %p) {\n")
|
||||
emith("entry:\n %r = call i32 @remove(ptr %p)\n %ok = icmp eq i32 %r, 0\n %z = zext i1 %ok to i32\n ret i32 %z\n}\n")
|
||||
|
||||
# copy: byte-for-byte via readall + a sized write. Returns 1 on success.
|
||||
emith("define i32 @fn_fs_copy(ptr %src, ptr %dst) {\n")
|
||||
emith("entry:\n %lp = alloca i32\n %buf = call ptr @fn_fs_readall(ptr %src, ptr %lp)\n %nz = icmp ne ptr %buf, null\n br i1 %nz, label %ok, label %bad\n")
|
||||
emith("define i32 @lp_fs_copy(ptr %src, ptr %dst) {\n")
|
||||
emith("entry:\n %lp = alloca i32\n %buf = call ptr @lp_fs_readall(ptr %src, ptr %lp)\n %nz = icmp ne ptr %buf, null\n br i1 %nz, label %ok, label %bad\n")
|
||||
emith("bad:\n ret i32 0\n")
|
||||
emith("ok:\n %tmp = call ptr @fn_path_join_ext(ptr %dst, ptr @fn_str_dottmp)\n %f = call ptr @fopen(ptr %tmp, ptr @fn_str_wb)\n %fnz = icmp ne ptr %f, null\n br i1 %fnz, label %w, label %bad\n")
|
||||
emith("ok:\n %tmp = call ptr @lp_path_join_ext(ptr %dst, ptr @fn_str_dottmp)\n %f = call ptr @fopen(ptr %tmp, ptr @fn_str_wb)\n %fnz = icmp ne ptr %f, null\n br i1 %fnz, label %w, label %bad\n")
|
||||
emith("w:\n %n = load i32, ptr %lp\n %nz64 = zext i32 %n to i64\n call i64 @fwrite(ptr %buf, i64 1, i64 %nz64, ptr %f)\n call i32 @fclose(ptr %f)\n %rr = call i32 @rename(ptr %tmp, ptr %dst)\n %ok2 = icmp eq i32 %rr, 0\n %z = zext i1 %ok2 to i32\n ret i32 %z\n}\n")
|
||||
|
||||
# mkdir: create %p and any missing parents (mkdir -p). Returns 1 if the
|
||||
# directory exists afterwards. Intermediate EEXIST errors are ignored.
|
||||
emith("define i32 @fn_fs_mkdir(ptr %p) {\n")
|
||||
emith("entry:\n %dup = call ptr @fn_fs_strdup(ptr %p)\n %ip = alloca i32\n store i32 1, ptr %ip\n br label %lp\n")
|
||||
emith("define i32 @lp_fs_mkdir(ptr %p) {\n")
|
||||
emith("entry:\n %dup = call ptr @lp_fs_strdup(ptr %p)\n %ip = alloca i32\n store i32 1, ptr %ip\n br label %lp\n")
|
||||
emith("lp:\n %i = load i32, ptr %ip\n %pp = getelementptr i8, ptr %dup, i32 %i\n %c = load i8, ptr %pp\n %ci = zext i8 %c to i32\n %z = icmp eq i32 %ci, 0\n br i1 %z, label %fin, label %go\n")
|
||||
emith("go:\n %sl = icmp eq i32 %ci, 47\n br i1 %sl, label %cut, label %nx\n")
|
||||
emith("cut:\n store i8 0, ptr %pp\n call i32 @mkdir(ptr %dup, i32 493)\n store i8 47, ptr %pp\n br label %nx\n")
|
||||
emith("nx:\n %i1 = add i32 %i, 1\n store i32 %i1, ptr %ip\n br label %lp\n")
|
||||
emith("fin:\n call i32 @mkdir(ptr %dup, i32 493)\n %r = call i32 @fn_fs_is_dir(ptr %p)\n ret i32 %r\n}\n")
|
||||
emith("fin:\n call i32 @mkdir(ptr %dup, i32 493)\n %r = call i32 @lp_fs_is_dir(ptr %p)\n ret i32 %r\n}\n")
|
||||
}
|
||||
|
||||
# ---- Fs.list (+ sort) ------------------------------------------------------
|
||||
|
|
@ -314,7 +314,7 @@ function emit_fs_dir() -> void {
|
|||
# by byte value for a stable, reproducible order. Each name is copied out of
|
||||
# the readdir buffer before the next call. macOS/BSD dirent: d_name at offset
|
||||
# 21 (documented native layout).
|
||||
emith("define ptr @fn_fs_list(ptr %path) {\n")
|
||||
emith("define ptr @lp_fs_list(ptr %path) {\n")
|
||||
emith("entry:\n %h = call ptr @malloc(i64 16)\n %d0 = getelementptr inbounds %LSlice, ptr %h, i32 0, i32 0\n %d1 = getelementptr inbounds %LSlice, ptr %h, i32 0, i32 1\n %d2 = getelementptr inbounds %LSlice, ptr %h, i32 0, i32 2\n")
|
||||
emith(" %datap = alloca ptr\n %cntp = alloca i32\n %capp = alloca i32\n %init = call ptr @malloc(i64 128)\n store ptr %init, ptr %datap\n store i32 0, ptr %cntp\n store i32 16, ptr %capp\n")
|
||||
emith(" %dir = call ptr @opendir(ptr %path)\n %dnz = icmp ne ptr %dir, null\n br i1 %dnz, label %rd, label %empty\n")
|
||||
|
|
@ -325,7 +325,7 @@ function emit_fs_dir() -> void {
|
|||
emith("chkdots:\n %n1p = getelementptr i8, ptr %namep, i32 1\n %n1 = load i8, ptr %n1p\n %n1i = zext i8 %n1 to i32\n %n1z = icmp eq i32 %n1i, 0\n br i1 %n1z, label %rd, label %chkdd\n") # "." -> skip
|
||||
emith("chkdd:\n %isdot1 = icmp eq i32 %n1i, 46\n br i1 %isdot1, label %chkdd2, label %keep\n")
|
||||
emith("chkdd2:\n %n2p = getelementptr i8, ptr %namep, i32 2\n %n2 = load i8, ptr %n2p\n %n2i = zext i8 %n2 to i32\n %n2z = icmp eq i32 %n2i, 0\n br i1 %n2z, label %rd, label %keep\n") # ".." -> skip
|
||||
emith("keep:\n %nm = call ptr @fn_fs_strdup(ptr %namep)\n")
|
||||
emith("keep:\n %nm = call ptr @lp_fs_strdup(ptr %namep)\n")
|
||||
# grow if full
|
||||
emith(" %cnt = load i32, ptr %cntp\n %cap = load i32, ptr %capp\n %full = icmp sge i32 %cnt, %cap\n br i1 %full, label %grow, label %put\n")
|
||||
emith("grow:\n %nc = mul i32 %cap, 2\n store i32 %nc, ptr %capp\n %ncz = zext i32 %nc to i64\n %nb = mul i64 %ncz, 8\n %old = load ptr, ptr %datap\n %new = call ptr @realloc(ptr %old, i64 %nb)\n store ptr %new, ptr %datap\n br label %put\n")
|
||||
|
|
@ -376,7 +376,7 @@ function emit_fs_mime() -> void {
|
|||
# create the string constants FIRST (as module globals), then reference them
|
||||
let en = new []pointer; let tn = new []pointer
|
||||
var i = 0
|
||||
while i < len(exts) { push(en, emit_str_const(exts[i])); push(tn, emit_str_const(tys[i])); i = i + 1 }
|
||||
while i < len(exts) { push(en, emit_str_const(exts[i])); push(tn, emit_str_const(tys[i])); i += 1 }
|
||||
let k_octet = emit_str_const("application/octet-stream")
|
||||
let n = len(exts)
|
||||
|
||||
|
|
@ -386,21 +386,21 @@ function emit_fs_mime() -> void {
|
|||
while i < len(exts) {
|
||||
if i > 0 { emith(", ") }
|
||||
emith(`{{ ptr, ptr }} {{ ptr {en[i]}, ptr {tn[i]} }}`)
|
||||
i = i + 1
|
||||
i += 1
|
||||
}
|
||||
emith("]\n")
|
||||
|
||||
# of(path): lowercased extension, then a linear scan of the table
|
||||
emith("define ptr @fn_mime_of(ptr %path) {\n")
|
||||
emith("entry:\n %ext = call ptr @fn_path_ext(ptr %path)\n %lc = call ptr @fn_mime_lc(ptr %ext)\n %ip = alloca i32\n store i32 0, ptr %ip\n br label %lp\n")
|
||||
emith("define ptr @lp_mime_of(ptr %path) {\n")
|
||||
emith("entry:\n %ext = call ptr @lp_path_ext(ptr %path)\n %lc = call ptr @lp_mime_lc(ptr %ext)\n %ip = alloca i32\n store i32 0, ptr %ip\n br label %lp\n")
|
||||
emith(`lp:\n %i = load i32, ptr %ip\n %lt = icmp slt i32 %i, {itoa(n)}\n br i1 %lt, label %body, label %def\n`)
|
||||
emith(`body:\n %kp = getelementptr [{itoa(n)} x {{ ptr, ptr }}], ptr @mime_tbl, i32 0, i32 %i, i32 0\n %k = load ptr, ptr %kp\n %c = call i32 @strcmp(ptr %lc, ptr %k)\n %eq = icmp eq i32 %c, 0\n br i1 %eq, label %hit, label %nx\n`)
|
||||
emith(`hit:\n %vp = getelementptr [{itoa(n)} x {{ ptr, ptr }}], ptr @mime_tbl, i32 0, i32 %i, i32 1\n %v = load ptr, ptr %vp\n %r = call ptr @fn_fs_strdup(ptr %v)\n ret ptr %r\n`)
|
||||
emith(`hit:\n %vp = getelementptr [{itoa(n)} x {{ ptr, ptr }}], ptr @mime_tbl, i32 0, i32 %i, i32 1\n %v = load ptr, ptr %vp\n %r = call ptr @lp_fs_strdup(ptr %v)\n ret ptr %r\n`)
|
||||
emith("nx:\n %i1 = add i32 %i, 1\n store i32 %i1, ptr %ip\n br label %lp\n")
|
||||
emith(`def:\n %d = call ptr @fn_fs_strdup(ptr {k_octet})\n ret ptr %d\n}}\n`)
|
||||
emith(`def:\n %d = call ptr @lp_fs_strdup(ptr {k_octet})\n ret ptr %d\n}}\n`)
|
||||
|
||||
# lowercase an extension, dropping a leading '.' (ASCII only, for table lookup)
|
||||
emith("define ptr @fn_mime_lc(ptr %s) {\n")
|
||||
emith("define ptr @lp_mime_lc(ptr %s) {\n")
|
||||
emith("entry:\n %l64 = call i64 @strlen(ptr %s)\n %l = trunc i64 %l64 to i32\n %cap = add i64 %l64, 1\n %out = call ptr @malloc(i64 %cap)\n")
|
||||
emith(" %b0 = load i8, ptr %s\n %b0i = zext i8 %b0 to i32\n %isdot = icmp eq i32 %b0i, 46\n %start = select i1 %isdot, i32 1, i32 0\n")
|
||||
emith(" %ip = alloca i32\n %op = alloca i32\n store i32 %start, ptr %ip\n store i32 0, ptr %op\n br label %lp\n")
|
||||
|
|
@ -414,23 +414,23 @@ function emit_fs_mime() -> void {
|
|||
# sniff(path): read the first bytes and recognise a few well-known signatures,
|
||||
# otherwise fall back to the extension. Covers PNG/JPEG/GIF/PDF for now.
|
||||
function emit_fs_sniff() -> void {
|
||||
emith("define ptr @fn_mime_sniff(ptr %path) {\n")
|
||||
emith("define ptr @lp_mime_sniff(ptr %path) {\n")
|
||||
emith("entry:\n %f = call ptr @fopen(ptr %path, ptr @fn_str_rb)\n %nz = icmp ne ptr %f, null\n br i1 %nz, label %ok, label %fallback\n")
|
||||
emith("ok:\n %buf = call ptr @malloc(i64 16)\n %rd = call i64 @fread(ptr %buf, i64 1, i64 8, ptr %f)\n call i32 @fclose(ptr %f)\n %rdi = trunc i64 %rd to i32\n %has4 = icmp sge i32 %rdi, 4\n br i1 %has4, label %chk, label %fallback\n")
|
||||
emith("chk:\n %b0p = getelementptr i8, ptr %buf, i32 0\n %b0 = load i8, ptr %b0p\n %b0i = zext i8 %b0 to i32\n %b1p = getelementptr i8, ptr %buf, i32 1\n %b1 = load i8, ptr %b1p\n %b1i = zext i8 %b1 to i32\n %b2p = getelementptr i8, ptr %buf, i32 2\n %b2 = load i8, ptr %b2p\n %b2i = zext i8 %b2 to i32\n %b3p = getelementptr i8, ptr %buf, i32 3\n %b3 = load i8, ptr %b3p\n %b3i = zext i8 %b3 to i32\n")
|
||||
# PNG: 89 50 4E 47
|
||||
emith(" %p0 = icmp eq i32 %b0i, 137\n %p1 = icmp eq i32 %b1i, 80\n %p2 = icmp eq i32 %b2i, 78\n %p3 = icmp eq i32 %b3i, 71\n %pa = and i1 %p0, %p1\n %pb = and i1 %pa, %p2\n %pc = and i1 %pb, %p3\n br i1 %pc, label %png, label %cj\n")
|
||||
emith("png:\n %rpng = call ptr @fn_fs_strdup(ptr @fn_sig_png)\n ret ptr %rpng\n")
|
||||
emith("png:\n %rpng = call ptr @lp_fs_strdup(ptr @fn_sig_png)\n ret ptr %rpng\n")
|
||||
# JPEG: FF D8 FF
|
||||
emith("cj:\n %j0 = icmp eq i32 %b0i, 255\n %j1 = icmp eq i32 %b1i, 216\n %j2 = icmp eq i32 %b2i, 255\n %ja = and i1 %j0, %j1\n %jb = and i1 %ja, %j2\n br i1 %jb, label %jpg, label %cg\n")
|
||||
emith("jpg:\n %rjpg = call ptr @fn_fs_strdup(ptr @fn_sig_jpg)\n ret ptr %rjpg\n")
|
||||
emith("jpg:\n %rjpg = call ptr @lp_fs_strdup(ptr @fn_sig_jpg)\n ret ptr %rjpg\n")
|
||||
# GIF: 47 49 46
|
||||
emith("cg:\n %g0 = icmp eq i32 %b0i, 71\n %g1 = icmp eq i32 %b1i, 73\n %g2 = icmp eq i32 %b2i, 70\n %ga = and i1 %g0, %g1\n %gb = and i1 %ga, %g2\n br i1 %gb, label %gif, label %cp\n")
|
||||
emith("gif:\n %rgif = call ptr @fn_fs_strdup(ptr @fn_sig_gif)\n ret ptr %rgif\n")
|
||||
emith("gif:\n %rgif = call ptr @lp_fs_strdup(ptr @fn_sig_gif)\n ret ptr %rgif\n")
|
||||
# PDF: 25 50 44 46
|
||||
emith("cp:\n %q0 = icmp eq i32 %b0i, 37\n %q1 = icmp eq i32 %b1i, 80\n %q2 = icmp eq i32 %b2i, 68\n %q3 = icmp eq i32 %b3i, 70\n %qa = and i1 %q0, %q1\n %qb = and i1 %qa, %q2\n %qc = and i1 %qb, %q3\n br i1 %qc, label %pdf, label %fallback\n")
|
||||
emith("pdf:\n %rpdf = call ptr @fn_fs_strdup(ptr @fn_sig_pdf)\n ret ptr %rpdf\n")
|
||||
emith("fallback:\n %r = call ptr @fn_mime_of(ptr %path)\n ret ptr %r\n}\n")
|
||||
emith("pdf:\n %rpdf = call ptr @lp_fs_strdup(ptr @fn_sig_pdf)\n ret ptr %rpdf\n")
|
||||
emith("fallback:\n %r = call ptr @lp_mime_of(ptr %path)\n ret ptr %r\n}\n")
|
||||
|
||||
# the small string constants the Fs/Path/Mime runtime references
|
||||
emith("@fn_str_rb = private unnamed_addr constant [3 x i8] c\"rb\\00\"\n")
|
||||
|
|
|
|||
|
|
@ -50,12 +50,12 @@ function emit_hash_ns(meth: pointer, e: Node) -> Val {
|
|||
if (meth == "of") or (meth == "fnv1a") { # FNV-1a 32-bit over the bytes
|
||||
g_uses_hashrt = true
|
||||
let s = emit_expr(e.kids[0])
|
||||
return val(emit_bind(`call i32 @fn_hash_fnv1a(ptr {s.code})`), "int")
|
||||
return val(emit_bind(`call i32 @lp_hash_fnv1a(ptr {s.code})`), "int")
|
||||
}
|
||||
if (meth == "of64") or (meth == "fnv1a_64") { # FNV-1a 64-bit -> a `long`
|
||||
g_uses_hashrt = true
|
||||
let s = emit_expr(e.kids[0])
|
||||
return val(emit_bind(`call i64 @fn_hash_fnv1a_64(ptr {s.code})`), "long")
|
||||
return val(emit_bind(`call i64 @lp_hash_fnv1a_64(ptr {s.code})`), "long")
|
||||
}
|
||||
if (meth == "mix64") { # fmix64 avalanche of one long
|
||||
let x = emit_expr(e.kids[0])
|
||||
|
|
@ -64,7 +64,7 @@ function emit_hash_ns(meth: pointer, e: Node) -> Val {
|
|||
if (meth == "crc32") { # CRC-32 (IEEE) checksum
|
||||
g_uses_hashrt = true
|
||||
let s = emit_expr(e.kids[0])
|
||||
return val(emit_bind(`call i32 @fn_hash_crc32(ptr {s.code})`), "int")
|
||||
return val(emit_bind(`call i32 @lp_hash_crc32(ptr {s.code})`), "int")
|
||||
}
|
||||
if (meth == "mix") { # fmix32 avalanche of one int
|
||||
let x = emit_expr(e.kids[0])
|
||||
|
|
@ -84,7 +84,7 @@ function emit_hash_ns(meth: pointer, e: Node) -> Val {
|
|||
let t1 = emit_bind(`add i32 {g}, {sl}`)
|
||||
let t2 = emit_bind(`add i32 {t1}, {sr}`)
|
||||
seed = emit_bind(`xor i32 {seed}, {t2}`)
|
||||
i = i + 1
|
||||
i += 1
|
||||
}
|
||||
return val(seed, "int")
|
||||
}
|
||||
|
|
@ -95,7 +95,7 @@ function emit_hash_ns(meth: pointer, e: Node) -> Val {
|
|||
# prime, and a bitwise CRC-32 with the reflected poly 0xEDB88320. No libc, no
|
||||
# allocation, bit-identical on every target.
|
||||
function emit_hash_prelude() -> void {
|
||||
emith("define i32 @fn_hash_fnv1a(ptr %s) {\n")
|
||||
emith("define i32 @lp_hash_fnv1a(ptr %s) {\n")
|
||||
emith("entry:\n")
|
||||
emith(" %hp = alloca i32\n")
|
||||
emith(" store i32 -2128831035, ptr %hp\n") # 0x811c9dc5 offset basis
|
||||
|
|
@ -121,7 +121,7 @@ function emit_hash_prelude() -> void {
|
|||
emith(" %hr = load i32, ptr %hp\n")
|
||||
emith(" ret i32 %hr\n")
|
||||
emith("}\n")
|
||||
emith("define i64 @fn_hash_fnv1a_64(ptr %s) {\n") # 64-bit FNV-1a, same shape, i64
|
||||
emith("define i64 @lp_hash_fnv1a_64(ptr %s) {\n") # 64-bit FNV-1a, same shape, i64
|
||||
emith("entry:\n")
|
||||
emith(" %hp = alloca i64\n")
|
||||
emith(" store i64 -3750763034362895579, ptr %hp\n") # 0xcbf29ce484222325 offset basis
|
||||
|
|
@ -147,7 +147,7 @@ function emit_hash_prelude() -> void {
|
|||
emith(" %hr = load i64, ptr %hp\n")
|
||||
emith(" ret i64 %hr\n")
|
||||
emith("}\n")
|
||||
emith("define i32 @fn_hash_crc32(ptr %s) {\n")
|
||||
emith("define i32 @lp_hash_crc32(ptr %s) {\n")
|
||||
emith("entry:\n")
|
||||
emith(" %cp = alloca i32\n")
|
||||
emith(" store i32 -1, ptr %cp\n") # init 0xFFFFFFFF
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ function key_lookup(name: pointer) -> int {
|
|||
# a single capital letter names that letter key: Key.A .. Key.Z
|
||||
if (name[1] == 0) {
|
||||
let c = name[0]
|
||||
if (c >= 65) and (c <= 90) { return c + 32 }
|
||||
if (c >= 'A') and (c <= 'Z') { return c + 32 }
|
||||
}
|
||||
return 0 - 1
|
||||
return -1
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,9 +33,9 @@ function is_log_ns(meth: pointer) -> bool {
|
|||
# and an int are converted the same way the `string(...)` builtin does.
|
||||
function log_stringify(v: Val) -> pointer {
|
||||
if (llty(v.ty) == "ptr") { return v.code }
|
||||
if (llty(v.ty) == "i64") { g_uses_longstr = true; return emit_bind(`call ptr @fn_long_str(i64 {v.code})`) }
|
||||
if (llty(v.ty) == "i64") { g_uses_longstr = true; return emit_bind(`call ptr @lp_long_str(i64 {v.code})`) }
|
||||
g_uses_intstr = true
|
||||
return emit_bind(`call ptr @fn_int_str(i32 {v.code})`)
|
||||
return emit_bind(`call ptr @lp_int_str(i32 {v.code})`)
|
||||
}
|
||||
|
||||
function emit_log_ns(meth: pointer, e: Node) -> Val {
|
||||
|
|
@ -67,19 +67,19 @@ function emit_log_ns(meth: pointer, e: Node) -> Val {
|
|||
line = emit_str_op("+", line, val(log_stringify(k), "string"))
|
||||
line = emit_str_op("+", line, val(emit_str_const("="), "string"))
|
||||
line = emit_str_op("+", line, val(log_stringify(v), "string"))
|
||||
i = i + 2
|
||||
i += 2
|
||||
}
|
||||
emit(` call void @fn_log_emit(i32 {lvl}, ptr {line.code})\n`)
|
||||
emit(` call void @lp_log_emit(i32 {lvl}, ptr {line.code})\n`)
|
||||
return val("0", "void")
|
||||
}
|
||||
|
||||
# emit_log_prelude — the log level register and the console sink, emitted once per
|
||||
# program that uses Log.* (g_uses_logrt). @fn_log_emit checks the threshold and,
|
||||
# program that uses Log.* (g_uses_logrt). @lp_log_emit checks the threshold and,
|
||||
# if the message is at or above it, writes the line + newline to stderr.
|
||||
function emit_log_prelude() -> void {
|
||||
emith("@L_log_level = global i32 0\n")
|
||||
emith("@.log_nl = private unnamed_addr constant [2 x i8] c\"\\0A\\00\"\n")
|
||||
emith("define void @fn_log_emit(i32 %lvl, ptr %s) {\n")
|
||||
emith("define void @lp_log_emit(i32 %lvl, ptr %s) {\n")
|
||||
emith("entry:\n %th = load i32, ptr @L_log_level\n %skip = icmp slt i32 %lvl, %th\n br i1 %skip, label %done, label %go\n")
|
||||
emith("go:\n %e = load ptr, ptr @__stderrp\n %n = call i64 @strlen(ptr %s)\n")
|
||||
emith(" %w = call i64 @fwrite(ptr %s, i64 1, i64 %n, ptr %e)\n %w2 = call i64 @fwrite(ptr @.log_nl, i64 1, i64 1, ptr %e)\n br label %done\n")
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
# emit_math.ludic — the Math.* namespace, all deterministic Q16.16 fixed-point.
|
||||
# min/max/abs/clamp lower to inline IR (and stay bare too); sign/floor/ceil/
|
||||
# round/lerp/inverse_lerp/remap and the geometry/interp helpers are inline; and
|
||||
# sqrt/sin/cos/tan call the runtime prelude below (@fn_fx_sqrt is a bit-by-bit
|
||||
# integer root, @fn_fx_sin a 256-entry interpolated sine table). Everything is
|
||||
# sqrt/sin/cos/tan call the runtime prelude below (@lp_fx_sqrt is a bit-by-bit
|
||||
# integer root, @lp_fx_sin a 256-entry interpolated sine table). Everything is
|
||||
# plain integer IR, so it is bit-identical on every platform.
|
||||
|
||||
function is_math_builtin(name: pointer) -> bool {
|
||||
|
|
@ -115,54 +115,54 @@ function emit_math_ns(meth: pointer, e: Node) -> Val {
|
|||
if (meth == "sqrt") { # sqrt(fixed) -> fixed (deterministic isqrt)
|
||||
g_uses_mathrt = true
|
||||
let a = emit_expr(e.kids[0])
|
||||
return val(emit_bind(`call i32 @fn_fx_sqrt(i32 {a.code})`), "fixed")
|
||||
return val(emit_bind(`call i32 @lp_fx_sqrt(i32 {a.code})`), "fixed")
|
||||
}
|
||||
if (meth == "sin") { # sin(radians: fixed) -> fixed
|
||||
g_uses_mathrt = true
|
||||
let a = emit_expr(e.kids[0])
|
||||
return val(emit_bind(`call i32 @fn_fx_sin(i32 {a.code})`), "fixed")
|
||||
return val(emit_bind(`call i32 @lp_fx_sin(i32 {a.code})`), "fixed")
|
||||
}
|
||||
if (meth == "cos") { # cos(x) = sin(x + pi/2), pi/2 = 102944 fixed
|
||||
g_uses_mathrt = true
|
||||
let a = emit_expr(e.kids[0])
|
||||
let sh = emit_bind(`add i32 {a.code}, 102944`)
|
||||
return val(emit_bind(`call i32 @fn_fx_sin(i32 {sh})`), "fixed")
|
||||
return val(emit_bind(`call i32 @lp_fx_sin(i32 {sh})`), "fixed")
|
||||
}
|
||||
if (meth == "tan") { # tan(x) = sin(x) / cos(x)
|
||||
g_uses_mathrt = true
|
||||
let a = emit_expr(e.kids[0])
|
||||
let s = emit_bind(`call i32 @fn_fx_sin(i32 {a.code})`)
|
||||
let s = emit_bind(`call i32 @lp_fx_sin(i32 {a.code})`)
|
||||
let sh = emit_bind(`add i32 {a.code}, 102944`)
|
||||
let c = emit_bind(`call i32 @fn_fx_sin(i32 {sh})`)
|
||||
let c = emit_bind(`call i32 @lp_fx_sin(i32 {sh})`)
|
||||
return val(fx_div_code(s, c), "fixed")
|
||||
}
|
||||
if (meth == "atan2") { # atan2(y, x) -> angle in radians
|
||||
g_uses_mathrt = true
|
||||
let y = emit_expr(e.kids[0]); let x = emit_expr(e.kids[1])
|
||||
return val(emit_bind(`call i32 @fn_fx_atan2(i32 {y.code}, i32 {x.code})`), "fixed")
|
||||
return val(emit_bind(`call i32 @lp_fx_atan2(i32 {y.code}, i32 {x.code})`), "fixed")
|
||||
}
|
||||
if (meth == "asin") { # asin(x) = atan2(x, sqrt(1 - x^2))
|
||||
g_uses_mathrt = true
|
||||
let x = emit_expr(e.kids[0])
|
||||
let xx = fx_mul_code(x.code, x.code)
|
||||
let om = emit_bind(`sub i32 65536, {xx}`)
|
||||
let root = emit_bind(`call i32 @fn_fx_sqrt(i32 {om})`)
|
||||
return val(emit_bind(`call i32 @fn_fx_atan2(i32 {x.code}, i32 {root})`), "fixed")
|
||||
let root = emit_bind(`call i32 @lp_fx_sqrt(i32 {om})`)
|
||||
return val(emit_bind(`call i32 @lp_fx_atan2(i32 {x.code}, i32 {root})`), "fixed")
|
||||
}
|
||||
if (meth == "acos") { # acos(x) = atan2(sqrt(1 - x^2), x)
|
||||
g_uses_mathrt = true
|
||||
let x = emit_expr(e.kids[0])
|
||||
let xx = fx_mul_code(x.code, x.code)
|
||||
let om = emit_bind(`sub i32 65536, {xx}`)
|
||||
let root = emit_bind(`call i32 @fn_fx_sqrt(i32 {om})`)
|
||||
return val(emit_bind(`call i32 @fn_fx_atan2(i32 {root}, i32 {x.code})`), "fixed")
|
||||
let root = emit_bind(`call i32 @lp_fx_sqrt(i32 {om})`)
|
||||
return val(emit_bind(`call i32 @lp_fx_atan2(i32 {root}, i32 {x.code})`), "fixed")
|
||||
}
|
||||
if (meth == "hypot") { # hypot(x, y) = sqrt(x*x + y*y)
|
||||
g_uses_mathrt = true
|
||||
let x = emit_expr(e.kids[0]); let y = emit_expr(e.kids[1])
|
||||
let xx = fx_mul_code(x.code, x.code); let yy = fx_mul_code(y.code, y.code)
|
||||
let s = emit_bind(`add i32 {xx}, {yy}`)
|
||||
return val(emit_bind(`call i32 @fn_fx_sqrt(i32 {s})`), "fixed")
|
||||
return val(emit_bind(`call i32 @lp_fx_sqrt(i32 {s})`), "fixed")
|
||||
}
|
||||
if (meth == "dist2") { # dist2(x0,y0,x1,y1) = dx*dx + dy*dy
|
||||
let x0 = emit_expr(e.kids[0]); let y0 = emit_expr(e.kids[1])
|
||||
|
|
@ -180,26 +180,26 @@ function emit_math_ns(meth: pointer, e: Node) -> Val {
|
|||
let dy = emit_bind(`sub i32 {y1.code}, {y0.code}`)
|
||||
let xx = fx_mul_code(dx, dx); let yy = fx_mul_code(dy, dy)
|
||||
let s = emit_bind(`add i32 {xx}, {yy}`)
|
||||
return val(emit_bind(`call i32 @fn_fx_sqrt(i32 {s})`), "fixed")
|
||||
return val(emit_bind(`call i32 @lp_fx_sqrt(i32 {s})`), "fixed")
|
||||
}
|
||||
if (meth == "exp") { # e^x = 2^(x * log2 e), log2 e = 94548 fixed
|
||||
g_uses_mathrt = true
|
||||
let a = emit_expr(e.kids[0])
|
||||
let t = fx_mul_code(a.code, "94548")
|
||||
return val(emit_bind(`call i32 @fn_fx_exp2(i32 {t})`), "fixed")
|
||||
return val(emit_bind(`call i32 @lp_fx_exp2(i32 {t})`), "fixed")
|
||||
}
|
||||
if (meth == "log") { # natural log: ln(x) = log2(x) * ln 2, ln 2 = 45426 fixed
|
||||
g_uses_mathrt = true
|
||||
let a = emit_expr(e.kids[0])
|
||||
let l2 = emit_bind(`call i32 @fn_fx_log2(i32 {a.code})`)
|
||||
let l2 = emit_bind(`call i32 @lp_fx_log2(i32 {a.code})`)
|
||||
return val(fx_mul_code(l2, "45426"), "fixed")
|
||||
}
|
||||
if (meth == "pow") { # a^b = 2^(b * log2 a); needs a > 0
|
||||
g_uses_mathrt = true
|
||||
let a = emit_expr(e.kids[0]); let b = emit_expr(e.kids[1])
|
||||
let l2 = emit_bind(`call i32 @fn_fx_log2(i32 {a.code})`)
|
||||
let l2 = emit_bind(`call i32 @lp_fx_log2(i32 {a.code})`)
|
||||
let t = fx_mul_code(b.code, l2)
|
||||
return val(emit_bind(`call i32 @fn_fx_exp2(i32 {t})`), "fixed")
|
||||
return val(emit_bind(`call i32 @lp_fx_exp2(i32 {t})`), "fixed")
|
||||
}
|
||||
if (meth == "deg_to_rad") { # d * (pi/180), pi/180 = 1144 fixed
|
||||
let d = emit_expr(e.kids[0])
|
||||
|
|
@ -279,14 +279,14 @@ function emit_math_ns(meth: pointer, e: Node) -> Val {
|
|||
}
|
||||
|
||||
# emit_math_prelude — the deterministic fixed-point math runtime, emitted once
|
||||
# per program that uses Math.sqrt/sin/cos/tan/exp/log/pow. @fn_fx_sqrt is a
|
||||
# 64-bit integer square root (bit-by-bit); @fn_fx_sin reads a 256-entry Q16.16
|
||||
# sine table with linear interpolation; @fn_fx_exp2/@fn_fx_log2 are range-reduced
|
||||
# per program that uses Math.sqrt/sin/cos/tan/exp/log/pow. @lp_fx_sqrt is a
|
||||
# 64-bit integer square root (bit-by-bit); @lp_fx_sin reads a 256-entry Q16.16
|
||||
# sine table with linear interpolation; @lp_fx_exp2/@lp_fx_log2 are range-reduced
|
||||
# Q16.16 polynomials (base-2 exp and log) that back exp/log/pow. All are pure
|
||||
# integer IR, so bit-identical on every platform.
|
||||
function emit_math_prelude() -> void {
|
||||
emith("@L_sin_tab = private unnamed_addr constant [256 x i32] [i32 0, i32 1608, i32 3216, i32 4821, i32 6424, i32 8022, i32 9616, i32 11204, i32 12785, i32 14359, i32 15924, i32 17479, i32 19024, i32 20557, i32 22078, i32 23586, i32 25080, i32 26558, i32 28020, i32 29466, i32 30893, i32 32303, i32 33692, i32 35062, i32 36410, i32 37736, i32 39040, i32 40320, i32 41576, i32 42806, i32 44011, i32 45190, i32 46341, i32 47464, i32 48559, i32 49624, i32 50660, i32 51665, i32 52639, i32 53581, i32 54491, i32 55368, i32 56212, i32 57022, i32 57798, i32 58538, i32 59244, i32 59914, i32 60547, i32 61145, i32 61705, i32 62228, i32 62714, i32 63162, i32 63572, i32 63944, i32 64277, i32 64571, i32 64827, i32 65043, i32 65220, i32 65358, i32 65457, i32 65516, i32 65536, i32 65516, i32 65457, i32 65358, i32 65220, i32 65043, i32 64827, i32 64571, i32 64277, i32 63944, i32 63572, i32 63162, i32 62714, i32 62228, i32 61705, i32 61145, i32 60547, i32 59914, i32 59244, i32 58538, i32 57798, i32 57022, i32 56212, i32 55368, i32 54491, i32 53581, i32 52639, i32 51665, i32 50660, i32 49624, i32 48559, i32 47464, i32 46341, i32 45190, i32 44011, i32 42806, i32 41576, i32 40320, i32 39040, i32 37736, i32 36410, i32 35062, i32 33692, i32 32303, i32 30893, i32 29466, i32 28020, i32 26558, i32 25080, i32 23586, i32 22078, i32 20557, i32 19024, i32 17479, i32 15924, i32 14359, i32 12785, i32 11204, i32 9616, i32 8022, i32 6424, i32 4821, i32 3216, i32 1608, i32 0, i32 -1608, i32 -3216, i32 -4821, i32 -6424, i32 -8022, i32 -9616, i32 -11204, i32 -12785, i32 -14359, i32 -15924, i32 -17479, i32 -19024, i32 -20557, i32 -22078, i32 -23586, i32 -25080, i32 -26558, i32 -28020, i32 -29466, i32 -30893, i32 -32303, i32 -33692, i32 -35062, i32 -36410, i32 -37736, i32 -39040, i32 -40320, i32 -41576, i32 -42806, i32 -44011, i32 -45190, i32 -46341, i32 -47464, i32 -48559, i32 -49624, i32 -50660, i32 -51665, i32 -52639, i32 -53581, i32 -54491, i32 -55368, i32 -56212, i32 -57022, i32 -57798, i32 -58538, i32 -59244, i32 -59914, i32 -60547, i32 -61145, i32 -61705, i32 -62228, i32 -62714, i32 -63162, i32 -63572, i32 -63944, i32 -64277, i32 -64571, i32 -64827, i32 -65043, i32 -65220, i32 -65358, i32 -65457, i32 -65516, i32 -65536, i32 -65516, i32 -65457, i32 -65358, i32 -65220, i32 -65043, i32 -64827, i32 -64571, i32 -64277, i32 -63944, i32 -63572, i32 -63162, i32 -62714, i32 -62228, i32 -61705, i32 -61145, i32 -60547, i32 -59914, i32 -59244, i32 -58538, i32 -57798, i32 -57022, i32 -56212, i32 -55368, i32 -54491, i32 -53581, i32 -52639, i32 -51665, i32 -50660, i32 -49624, i32 -48559, i32 -47464, i32 -46341, i32 -45190, i32 -44011, i32 -42806, i32 -41576, i32 -40320, i32 -39040, i32 -37736, i32 -36410, i32 -35062, i32 -33692, i32 -32303, i32 -30893, i32 -29466, i32 -28020, i32 -26558, i32 -25080, i32 -23586, i32 -22078, i32 -20557, i32 -19024, i32 -17479, i32 -15924, i32 -14359, i32 -12785, i32 -11204, i32 -9616, i32 -8022, i32 -6424, i32 -4821, i32 -3216, i32 -1608]\n")
|
||||
emith("define i32 @fn_fx_sqrt(i32 %x) {\n")
|
||||
emith("define i32 @lp_fx_sqrt(i32 %x) {\n")
|
||||
emith("entry:\n %neg = icmp slt i32 %x, 0\n br i1 %neg, label %ret0, label %go\n")
|
||||
emith("ret0:\n ret i32 0\n")
|
||||
emith("go:\n %x64 = sext i32 %x to i64\n %n0 = shl i64 %x64, 16\n")
|
||||
|
|
@ -300,14 +300,14 @@ function emit_math_prelude() -> void {
|
|||
emith("shift:\n %rsh2 = lshr i64 %r1, 1\n store i64 %rsh2, ptr %rp\n br label %next\n")
|
||||
emith("next:\n %b4 = lshr i64 %b3, 2\n store i64 %b4, ptr %bp\n br label %loop\n")
|
||||
emith("done:\n %rf = load i64, ptr %rp\n %r32 = trunc i64 %rf to i32\n ret i32 %r32\n}\n")
|
||||
emith("define i32 @fn_fx_sin(i32 %x) {\n")
|
||||
emith("define i32 @lp_fx_sin(i32 %x) {\n")
|
||||
emith(" %xe = sext i32 %x to i64\n %m = mul i64 %xe, 2670177\n %idxf = ashr i64 %m, 16\n")
|
||||
emith(" %i0 = ashr i64 %idxf, 16\n %i0m = and i64 %i0, 255\n %frac = and i64 %idxf, 65535\n")
|
||||
emith(" %i1 = add i64 %i0m, 1\n %i1m = and i64 %i1, 255\n")
|
||||
emith(" %p0 = getelementptr [256 x i32], ptr @L_sin_tab, i64 0, i64 %i0m\n %v0 = load i32, ptr %p0\n")
|
||||
emith(" %p1 = getelementptr [256 x i32], ptr @L_sin_tab, i64 0, i64 %i1m\n %v1 = load i32, ptr %p1\n")
|
||||
emith(" %d = sub i32 %v1, %v0\n %de = sext i32 %d to i64\n %dm = mul i64 %de, %frac\n %dsh = ashr i64 %dm, 16\n %dsh32 = trunc i64 %dsh to i32\n %res = add i32 %v0, %dsh32\n ret i32 %res\n}\n")
|
||||
emith("define i32 @fn_fx_atan2(i32 %y, i32 %x) {\n")
|
||||
emith("define i32 @lp_fx_atan2(i32 %y, i32 %x) {\n")
|
||||
emith("entry:\n")
|
||||
emith(" %xz = icmp eq i32 %x, 0\n")
|
||||
emith(" %yz = icmp eq i32 %y, 0\n")
|
||||
|
|
@ -363,11 +363,11 @@ function emit_math_prelude() -> void {
|
|||
emith(" %res = select i1 %yneg, i32 %angneg, i32 %angle\n")
|
||||
emith(" ret i32 %res\n")
|
||||
emith("}\n")
|
||||
# @fn_fx_exp2(x) = 2^x, Q16.16. Split x into integer part i and fraction f in
|
||||
# @lp_fx_exp2(x) = 2^x, Q16.16. Split x into integer part i and fraction f in
|
||||
# [0,1); 2^f is a 5th-order Taylor polynomial (Horner, coefficients (ln2)^k/k!
|
||||
# in Q16.16), then shift by i. Shift amounts are clamped to a safe [0,31] so a
|
||||
# huge exponent saturates instead of hitting an undefined shift.
|
||||
emith("define i32 @fn_fx_exp2(i32 %x) {\n")
|
||||
emith("define i32 @lp_fx_exp2(i32 %x) {\n")
|
||||
emith(" %i = ashr i32 %x, 16\n %f = and i32 %x, 65535\n %fe = sext i32 %f to i64\n")
|
||||
emith(" %m5 = mul i64 %fe, 87\n %s5 = ashr i64 %m5, 16\n %p5 = add i64 %s5, 630\n")
|
||||
emith(" %m4 = mul i64 %fe, %p5\n %s4 = ashr i64 %m4, 16\n %p4 = add i64 %s4, 3638\n")
|
||||
|
|
@ -381,12 +381,12 @@ function emit_math_prelude() -> void {
|
|||
emith(" %ni = sub i32 0, %i\n %ra0 = select i1 %ipos, i32 0, i32 %ni\n %rahi = icmp sgt i32 %ra0, 31\n %ra = select i1 %rahi, i32 31, i32 %ra0\n")
|
||||
emith(" %shr = ashr i32 %p, %ra\n")
|
||||
emith(" %res = select i1 %ipos, i32 %shl, i32 %shr\n ret i32 %res\n}\n")
|
||||
# @fn_fx_log2(x) = log2(x), Q16.16, for x > 0 (x <= 0 saturates to the most
|
||||
# @lp_fx_log2(x) = log2(x), Q16.16, for x > 0 (x <= 0 saturates to the most
|
||||
# negative i32). ctlz finds the MSB, giving the integer part e and a mantissa
|
||||
# m in [1,2); ln(m) uses the fast-converging atanh series on r = (m-1)/(m+1),
|
||||
# then log2(m) = ln(m)/ln2. Result is e + log2(m).
|
||||
emith("declare i32 @llvm.ctlz.i32(i32, i1)\n")
|
||||
emith("define i32 @fn_fx_log2(i32 %x) {\n")
|
||||
emith("define i32 @lp_fx_log2(i32 %x) {\n")
|
||||
emith("entry:\n %pos = icmp sgt i32 %x, 0\n br i1 %pos, label %go, label %neg\n")
|
||||
emith("neg:\n ret i32 -2147483648\n")
|
||||
emith("go:\n %lz = call i32 @llvm.ctlz.i32(i32 %x, i1 true)\n %pmsb = sub i32 31, %lz\n")
|
||||
|
|
|
|||
|
|
@ -31,10 +31,10 @@ function net_model_bytes(m: Node) -> int {
|
|||
let c = find_comp(m.kids[ci].s)
|
||||
if (c != null) {
|
||||
var fj = 0
|
||||
while fj < len(c.kids) { if c.kids[fj].ival == 1 { total = total + net_field_ibytes(c.kids[fj].ty) }; fj = fj + 1 }
|
||||
while fj < len(c.kids) { if c.kids[fj].ival == 1 { total += net_field_ibytes(c.kids[fj].ty) }; fj += 1 }
|
||||
}
|
||||
}
|
||||
ci = ci + 1
|
||||
ci += 1
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
|
@ -43,14 +43,14 @@ function net_model_syncs(m: Node) -> bool { return net_model_bytes(m) > 0 }
|
|||
|
||||
function net_has_sync() -> bool {
|
||||
var i = 0
|
||||
while i < len(prog) { if prog[i].kind == N_ARCH { if net_model_syncs(prog[i]) { return true } }; i = i + 1 }
|
||||
while i < len(prog) { if prog[i].kind == N_ARCH { if net_model_syncs(prog[i]) { return true } }; i += 1 }
|
||||
return false
|
||||
}
|
||||
|
||||
# ---- N3: ownership -----------------------------------------------------------
|
||||
function net_has_owned() -> bool {
|
||||
var i = 0
|
||||
while i < len(prog) { if prog[i].kind == N_ARCH and (prog[i].ival == 1) { return true }; i = i + 1 }
|
||||
while i < len(prog) { if prog[i].kind == N_ARCH and (prog[i].ival == 1) { return true }; i += 1 }
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
@ -58,7 +58,7 @@ function net_has_owned() -> bool {
|
|||
# A handler tagged @Server (ival==1) or @Predicted (ival==2) has a network role.
|
||||
function net_has_role() -> bool {
|
||||
var i = 0
|
||||
while i < len(prog) { if prog[i].kind == N_SYS and (prog[i].ival != 0) { return true }; i = i + 1 }
|
||||
while i < len(prog) { if prog[i].kind == N_SYS and (prog[i].ival != 0) { return true }; i += 1 }
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
@ -95,15 +95,15 @@ function net_check() -> void {
|
|||
any = true
|
||||
if (llty(c.kids[fj].ty) == "ptr") { perr(`@Sync field {cn}.{c.kids[fj].s} is not a POD scalar (networked fields must be int/bool/fixed/byte)`) }
|
||||
}
|
||||
fj = fj + 1
|
||||
fj += 1
|
||||
}
|
||||
if not any { net_warn(`model {m.s} @Syncs {cn} but it has no @Sync fields — nothing replicates`) }
|
||||
}
|
||||
}
|
||||
ci = ci + 1
|
||||
ci += 1
|
||||
}
|
||||
}
|
||||
i = i + 1
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -129,13 +129,13 @@ function emit_net_serialize(m: Node) -> void {
|
|||
let fa = nreg(); emit(" "); emit(fa); emit(" = getelementptr inbounds %Cmp_"); emit(cn); emit(", ptr "); emit(s); emit(", i32 0, i32 "); emit(itoa(fj)); emit("\n")
|
||||
let dst = nreg(); emit(" "); emit(dst); emit(" = getelementptr inbounds i8, ptr %buf, i32 "); emit(itoa(off)); emit("\n")
|
||||
emit(" call ptr @memcpy(ptr "); emit(dst); emit(", ptr "); emit(fa); emit(", i64 "); emit(bytes); emit(")\n")
|
||||
off = off + net_field_ibytes(c.kids[fj].ty)
|
||||
off += net_field_ibytes(c.kids[fj].ty)
|
||||
}
|
||||
fj = fj + 1
|
||||
fj += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
ci = ci + 1
|
||||
ci += 1
|
||||
}
|
||||
emit(" ret i32 "); emit(itoa(off)); emit("\n}\n\n")
|
||||
}
|
||||
|
|
@ -162,13 +162,13 @@ function emit_net_apply(m: Node) -> void {
|
|||
let fa = nreg(); emit(" "); emit(fa); emit(" = getelementptr inbounds %Cmp_"); emit(cn); emit(", ptr "); emit(s); emit(", i32 0, i32 "); emit(itoa(fj)); emit("\n")
|
||||
let src = nreg(); emit(" "); emit(src); emit(" = getelementptr inbounds i8, ptr %buf, i32 "); emit(itoa(off)); emit("\n")
|
||||
emit(" call ptr @memcpy(ptr "); emit(fa); emit(", ptr "); emit(src); emit(", i64 "); emit(bytes); emit(")\n")
|
||||
off = off + net_field_ibytes(c.kids[fj].ty)
|
||||
off += net_field_ibytes(c.kids[fj].ty)
|
||||
}
|
||||
fj = fj + 1
|
||||
fj += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
ci = ci + 1
|
||||
ci += 1
|
||||
}
|
||||
emit(" ret void\n}\n\n")
|
||||
}
|
||||
|
|
@ -191,9 +191,9 @@ function emit_net_dispatch() -> void {
|
|||
emit(" br i1 %c"); emit(sk); emit(", label %h"); emit(sk); emit(", label %n"); emit(sk); emit("\n")
|
||||
emit("h"); emit(sk); emit(":\n %r"); emit(sk); emit(" = call i32 @L_serialize_"); emit(mn); emit("(i32 %e, ptr %buf)\n ret i32 %r"); emit(sk); emit("\n")
|
||||
emit("n"); emit(sk); emit(":\n")
|
||||
k = k + 1
|
||||
k += 1
|
||||
}
|
||||
i = i + 1
|
||||
i += 1
|
||||
}
|
||||
emit(" ret i32 0\n}\n\n")
|
||||
|
||||
|
|
@ -207,9 +207,9 @@ function emit_net_dispatch() -> void {
|
|||
emit(" br i1 %c"); emit(sk); emit(", label %h"); emit(sk); emit(", label %n"); emit(sk); emit("\n")
|
||||
emit("h"); emit(sk); emit(":\n call void @L_apply_"); emit(mn); emit("(i32 %e, ptr %buf, i32 %len)\n ret void\n")
|
||||
emit("n"); emit(sk); emit(":\n")
|
||||
k = k + 1
|
||||
k += 1
|
||||
}
|
||||
i = i + 1
|
||||
i += 1
|
||||
}
|
||||
emit(" ret void\n}\n\n")
|
||||
|
||||
|
|
@ -225,9 +225,9 @@ function emit_net_dispatch() -> void {
|
|||
emit(" br i1 %c"); emit(sk); emit(", label %h"); emit(sk); emit(", label %n"); emit(sk); emit("\n")
|
||||
emit("h"); emit(sk); emit(":\n ret i32 "); emit(itoa(net_model_bytes(prog[i]))); emit("\n")
|
||||
emit("n"); emit(sk); emit(":\n")
|
||||
k = k + 1
|
||||
k += 1
|
||||
}
|
||||
i = i + 1
|
||||
i += 1
|
||||
}
|
||||
emit(" ret i32 0\n}\n\n")
|
||||
}
|
||||
|
|
@ -296,14 +296,14 @@ function emit_loopback() -> void {
|
|||
|
||||
function net_has_remote() -> bool {
|
||||
var i = 0
|
||||
while i < len(g_events) { if (g_events[i].ty != null) { return true }; i = i + 1 }
|
||||
while i < len(g_events) { if (g_events[i].ty != null) { return true }; i += 1 }
|
||||
return false
|
||||
}
|
||||
# stable wire id for an event = its index in g_events (same program both peers)
|
||||
function net_event_id(name: pointer) -> int {
|
||||
var i = 0
|
||||
while i < len(g_events) { if (g_events[i].s == name) { return i }; i = i + 1 }
|
||||
return 0 - 1
|
||||
while i < len(g_events) { if (g_events[i].s == name) { return i }; i += 1 }
|
||||
return -1
|
||||
}
|
||||
# the transport symbols: an `extern fn` override, else the built-in loopback.
|
||||
function net_send_sym() -> pointer { let x = find_extern("net_send"); if (x != null) { return x.a.s }; return "L_net_send" }
|
||||
|
|
@ -338,14 +338,14 @@ function emit_net_pump() -> void {
|
|||
emit(" %fv"); emit(fk); emit(" = load "); emit(ft); emit(", ptr %fa"); emit(fk); emit("\n")
|
||||
if f > 0 { buf_puts(acc, ", ") }
|
||||
buf_puts(acc, ft); buf_puts(acc, " %fv"); buf_puts(acc, fk)
|
||||
off = off + net_field_ibytes(ev.kids[f].ty)
|
||||
f = f + 1
|
||||
off += net_field_ibytes(ev.kids[f].ty)
|
||||
f += 1
|
||||
}
|
||||
emit(" call void @ev_"); emit(ev.s); emit("("); emit(buf_str(acc)); emit(")\n")
|
||||
emit(" br label %loop\n")
|
||||
emit("x"); emit(sk); emit(":\n")
|
||||
}
|
||||
e = e + 1
|
||||
e += 1
|
||||
}
|
||||
emit(" br label %loop\n") # unknown id: skip, keep draining
|
||||
emit("fin:\n ret void\n}\n\n")
|
||||
|
|
@ -361,7 +361,7 @@ function emit_net() -> void {
|
|||
emit_net_serialize(prog[i])
|
||||
emit_net_apply(prog[i])
|
||||
}
|
||||
i = i + 1
|
||||
i += 1
|
||||
}
|
||||
emit_net_dispatch()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,114 +36,114 @@ function emit_noise_ns(meth: pointer, e: Node) -> Val {
|
|||
g_uses_noisert = true
|
||||
if (meth == "value2") {
|
||||
let x = emit_expr(e.kids[0]); let y = emit_expr(e.kids[1]); let s = emit_expr(e.kids[2])
|
||||
return val(emit_bind(`call i32 @fn_noise_value2(i32 {x.code}, i32 {y.code}, i32 {s.code})`), "fixed")
|
||||
return val(emit_bind(`call i32 @lp_noise_value2(i32 {x.code}, i32 {y.code}, i32 {s.code})`), "fixed")
|
||||
}
|
||||
if (meth == "perlin2") {
|
||||
let x = emit_expr(e.kids[0]); let y = emit_expr(e.kids[1]); let s = emit_expr(e.kids[2])
|
||||
return val(emit_bind(`call i32 @fn_noise_perlin2(i32 {x.code}, i32 {y.code}, i32 {s.code})`), "fixed")
|
||||
return val(emit_bind(`call i32 @lp_noise_perlin2(i32 {x.code}, i32 {y.code}, i32 {s.code})`), "fixed")
|
||||
}
|
||||
if (meth == "simplex2") {
|
||||
let x = emit_expr(e.kids[0]); let y = emit_expr(e.kids[1]); let s = emit_expr(e.kids[2])
|
||||
return val(emit_bind(`call i32 @fn_noise_simplex2(i32 {x.code}, i32 {y.code}, i32 {s.code})`), "fixed")
|
||||
return val(emit_bind(`call i32 @lp_noise_simplex2(i32 {x.code}, i32 {y.code}, i32 {s.code})`), "fixed")
|
||||
}
|
||||
if (meth == "fbm2") {
|
||||
g_uses_mathrt = true # simplex path is standalone; fbm needs fx_div only (local)
|
||||
let x = emit_expr(e.kids[0]); let y = emit_expr(e.kids[1]); let s = emit_expr(e.kids[2]); let o = emit_expr(e.kids[3])
|
||||
return val(emit_bind(`call i32 @fn_noise_fbm2(i32 {x.code}, i32 {y.code}, i32 {s.code}, i32 {o.code})`), "fixed")
|
||||
return val(emit_bind(`call i32 @lp_noise_fbm2(i32 {x.code}, i32 {y.code}, i32 {s.code}, i32 {o.code})`), "fixed")
|
||||
}
|
||||
if (meth == "cellular2") {
|
||||
g_uses_mathrt = true # F1 distance needs @fn_fx_sqrt
|
||||
g_uses_mathrt = true # F1 distance needs @lp_fx_sqrt
|
||||
let x = emit_expr(e.kids[0]); let y = emit_expr(e.kids[1]); let s = emit_expr(e.kids[2])
|
||||
return val(emit_bind(`call i32 @fn_noise_cellular2(i32 {x.code}, i32 {y.code}, i32 {s.code})`), "fixed")
|
||||
return val(emit_bind(`call i32 @lp_noise_cellular2(i32 {x.code}, i32 {y.code}, i32 {s.code})`), "fixed")
|
||||
}
|
||||
# cellular2_id: the hash id of the nearest feature cell (stable per cell -> use
|
||||
# it to pick a biome/material). Distances come from cellular2.
|
||||
let x = emit_expr(e.kids[0]); let y = emit_expr(e.kids[1]); let s = emit_expr(e.kids[2])
|
||||
return val(emit_bind(`call i32 @fn_noise_cellular2_id(i32 {x.code}, i32 {y.code}, i32 {s.code})`), "int")
|
||||
return val(emit_bind(`call i32 @lp_noise_cellular2_id(i32 {x.code}, i32 {y.code}, i32 {s.code})`), "int")
|
||||
}
|
||||
|
||||
# emit_noise_prelude — the noise runtime, emitted once per program that uses
|
||||
# Noise.* (g_uses_noisert). Pure Q16.16 integer IR; cellular/fbm additionally use
|
||||
# the math prelude (@fn_fx_sqrt), pulled in by setting g_uses_mathrt at the call.
|
||||
# the math prelude (@lp_fx_sqrt), pulled in by setting g_uses_mathrt at the call.
|
||||
function emit_noise_prelude() -> void {
|
||||
# Q16.16 helpers (local to noise so the prelude is self-contained for the
|
||||
# gradient/value paths). fx multiply, divide, lerp, and a [-1,1] clamp.
|
||||
emith("define i32 @fn_nfx_mul(i32 %a, i32 %b) {\n")
|
||||
emith("define i32 @lp_nfx_mul(i32 %a, i32 %b) {\n")
|
||||
emith(" %a64 = sext i32 %a to i64\n %b64 = sext i32 %b to i64\n %m = mul i64 %a64, %b64\n %s = ashr i64 %m, 16\n %r = trunc i64 %s to i32\n ret i32 %r\n}\n")
|
||||
emith("define i32 @fn_nfx_div(i32 %a, i32 %b) {\n")
|
||||
emith("define i32 @lp_nfx_div(i32 %a, i32 %b) {\n")
|
||||
emith(" %z = icmp eq i32 %b, 0\n br i1 %z, label %zero, label %go\n")
|
||||
emith("zero:\n ret i32 0\n")
|
||||
emith("go:\n %a64 = sext i32 %a to i64\n %ash = shl i64 %a64, 16\n %b64 = sext i32 %b to i64\n %d = sdiv i64 %ash, %b64\n %r = trunc i64 %d to i32\n ret i32 %r\n}\n")
|
||||
emith("define i32 @fn_nfx_lerp(i32 %a, i32 %b, i32 %t) {\n")
|
||||
emith(" %d = sub i32 %b, %a\n %dt = call i32 @fn_nfx_mul(i32 %d, i32 %t)\n %r = add i32 %a, %dt\n ret i32 %r\n}\n")
|
||||
emith("define i32 @fn_noise_clamp(i32 %v) {\n")
|
||||
emith("define i32 @lp_nfx_lerp(i32 %a, i32 %b, i32 %t) {\n")
|
||||
emith(" %d = sub i32 %b, %a\n %dt = call i32 @lp_nfx_mul(i32 %d, i32 %t)\n %r = add i32 %a, %dt\n ret i32 %r\n}\n")
|
||||
emith("define i32 @lp_noise_clamp(i32 %v) {\n")
|
||||
emith(" %hi = icmp sgt i32 %v, 65536\n %v1 = select i1 %hi, i32 65536, i32 %v\n %lo = icmp slt i32 %v1, -65536\n %r = select i1 %lo, i32 -65536, i32 %v1\n ret i32 %r\n}\n")
|
||||
|
||||
# integer lattice hash: mix seed + cell coords with large odd constants, then a
|
||||
# MurmurHash3-style fmix32 finalizer. Deterministic and well-distributed.
|
||||
emith("define i32 @fn_noise_hash(i32 %seed, i32 %xi, i32 %yi) {\n")
|
||||
emith("define i32 @lp_noise_hash(i32 %seed, i32 %xi, i32 %yi) {\n")
|
||||
emith(" %a = mul i32 %xi, 374761393\n %b = mul i32 %yi, -1028477387\n %c = add i32 %seed, %a\n %d0 = add i32 %c, %b\n")
|
||||
emith(" %e = lshr i32 %d0, 16\n %f = xor i32 %d0, %e\n %g = mul i32 %f, -2048144789\n")
|
||||
emith(" %h = lshr i32 %g, 13\n %i = xor i32 %g, %h\n %j = mul i32 %i, -1028477387\n")
|
||||
emith(" %k = lshr i32 %j, 16\n %l = xor i32 %j, %k\n ret i32 %l\n}\n")
|
||||
|
||||
# quintic fade 6t^5 - 15t^4 + 10t^3 (Q16.16); t in [0,1]
|
||||
emith("define i32 @fn_noise_fade(i32 %t) {\n")
|
||||
emith(" %t2 = call i32 @fn_nfx_mul(i32 %t, i32 %t)\n %t3 = call i32 @fn_nfx_mul(i32 %t2, i32 %t)\n")
|
||||
emith(" %t4 = call i32 @fn_nfx_mul(i32 %t3, i32 %t)\n %t5 = call i32 @fn_nfx_mul(i32 %t4, i32 %t)\n")
|
||||
emith("define i32 @lp_noise_fade(i32 %t) {\n")
|
||||
emith(" %t2 = call i32 @lp_nfx_mul(i32 %t, i32 %t)\n %t3 = call i32 @lp_nfx_mul(i32 %t2, i32 %t)\n")
|
||||
emith(" %t4 = call i32 @lp_nfx_mul(i32 %t3, i32 %t)\n %t5 = call i32 @lp_nfx_mul(i32 %t4, i32 %t)\n")
|
||||
emith(" %c6 = mul i32 %t5, 6\n %c15 = mul i32 %t4, 15\n %c10 = mul i32 %t3, 10\n")
|
||||
emith(" %s1 = sub i32 %c6, %c15\n %r = add i32 %s1, %c10\n ret i32 %r\n}\n")
|
||||
|
||||
# value noise: bilinear-interpolate the four corner random values (each mapped
|
||||
# to [-1,1]) with the faded fractional coordinates.
|
||||
emith("define i32 @fn_noise_value2(i32 %x, i32 %y, i32 %seed) {\n")
|
||||
emith("define i32 @lp_noise_value2(i32 %x, i32 %y, i32 %seed) {\n")
|
||||
emith(" %xi = ashr i32 %x, 16\n %yi = ashr i32 %y, 16\n %xf = and i32 %x, 65535\n %yf = and i32 %y, 65535\n")
|
||||
emith(" %u = call i32 @fn_noise_fade(i32 %xf)\n %v = call i32 @fn_noise_fade(i32 %yf)\n")
|
||||
emith(" %u = call i32 @lp_noise_fade(i32 %xf)\n %v = call i32 @lp_noise_fade(i32 %yf)\n")
|
||||
emith(" %xi1 = add i32 %xi, 1\n %yi1 = add i32 %yi, 1\n")
|
||||
emith(" %h00 = call i32 @fn_noise_hash(i32 %seed, i32 %xi, i32 %yi)\n %m00 = and i32 %h00, 131071\n %n00 = sub i32 %m00, 65536\n")
|
||||
emith(" %h10 = call i32 @fn_noise_hash(i32 %seed, i32 %xi1, i32 %yi)\n %m10 = and i32 %h10, 131071\n %n10 = sub i32 %m10, 65536\n")
|
||||
emith(" %h01 = call i32 @fn_noise_hash(i32 %seed, i32 %xi, i32 %yi1)\n %m01 = and i32 %h01, 131071\n %n01 = sub i32 %m01, 65536\n")
|
||||
emith(" %h11 = call i32 @fn_noise_hash(i32 %seed, i32 %xi1, i32 %yi1)\n %m11 = and i32 %h11, 131071\n %n11 = sub i32 %m11, 65536\n")
|
||||
emith(" %a = call i32 @fn_nfx_lerp(i32 %n00, i32 %n10, i32 %u)\n %b = call i32 @fn_nfx_lerp(i32 %n01, i32 %n11, i32 %u)\n")
|
||||
emith(" %n = call i32 @fn_nfx_lerp(i32 %a, i32 %b, i32 %v)\n %r = call i32 @fn_noise_clamp(i32 %n)\n ret i32 %r\n}\n")
|
||||
emith(" %h00 = call i32 @lp_noise_hash(i32 %seed, i32 %xi, i32 %yi)\n %m00 = and i32 %h00, 131071\n %n00 = sub i32 %m00, 65536\n")
|
||||
emith(" %h10 = call i32 @lp_noise_hash(i32 %seed, i32 %xi1, i32 %yi)\n %m10 = and i32 %h10, 131071\n %n10 = sub i32 %m10, 65536\n")
|
||||
emith(" %h01 = call i32 @lp_noise_hash(i32 %seed, i32 %xi, i32 %yi1)\n %m01 = and i32 %h01, 131071\n %n01 = sub i32 %m01, 65536\n")
|
||||
emith(" %h11 = call i32 @lp_noise_hash(i32 %seed, i32 %xi1, i32 %yi1)\n %m11 = and i32 %h11, 131071\n %n11 = sub i32 %m11, 65536\n")
|
||||
emith(" %a = call i32 @lp_nfx_lerp(i32 %n00, i32 %n10, i32 %u)\n %b = call i32 @lp_nfx_lerp(i32 %n01, i32 %n11, i32 %u)\n")
|
||||
emith(" %n = call i32 @lp_nfx_lerp(i32 %a, i32 %b, i32 %v)\n %r = call i32 @lp_noise_clamp(i32 %n)\n ret i32 %r\n}\n")
|
||||
|
||||
# 8 gradient directions (axis + diagonal, the diagonals scaled by 1/sqrt2), as
|
||||
# packed (gx, gy) Q16.16 pairs; grad2 dots the selected gradient with (dx, dy).
|
||||
emith("@noise_grad2 = private unnamed_addr constant [16 x i32] [i32 65536, i32 0, i32 -65536, i32 0, i32 0, i32 65536, i32 0, i32 -65536, i32 46341, i32 46341, i32 -46341, i32 46341, i32 46341, i32 -46341, i32 -46341, i32 -46341]\n")
|
||||
emith("define i32 @fn_noise_grad2(i32 %hash, i32 %dx, i32 %dy) {\n")
|
||||
emith("define i32 @lp_noise_grad2(i32 %hash, i32 %dx, i32 %dy) {\n")
|
||||
emith(" %h = and i32 %hash, 7\n %idx = shl i32 %h, 1\n %idx64 = sext i32 %idx to i64\n")
|
||||
emith(" %gxp = getelementptr [16 x i32], ptr @noise_grad2, i64 0, i64 %idx64\n %gx = load i32, ptr %gxp\n")
|
||||
emith(" %idy = add i32 %idx, 1\n %idy64 = sext i32 %idy to i64\n %gyp = getelementptr [16 x i32], ptr @noise_grad2, i64 0, i64 %idy64\n %gy = load i32, ptr %gyp\n")
|
||||
emith(" %px = call i32 @fn_nfx_mul(i32 %gx, i32 %dx)\n %py = call i32 @fn_nfx_mul(i32 %gy, i32 %dy)\n %r = add i32 %px, %py\n ret i32 %r\n}\n")
|
||||
emith(" %px = call i32 @lp_nfx_mul(i32 %gx, i32 %dx)\n %py = call i32 @lp_nfx_mul(i32 %gy, i32 %dy)\n %r = add i32 %px, %py\n ret i32 %r\n}\n")
|
||||
|
||||
# Perlin gradient noise: interpolate the four corner gradient dots, then scale
|
||||
# the ~[-0.707,0.707] result by sqrt2 into [-1,1] (and clamp for safety).
|
||||
emith("define i32 @fn_noise_perlin2(i32 %x, i32 %y, i32 %seed) {\n")
|
||||
emith("define i32 @lp_noise_perlin2(i32 %x, i32 %y, i32 %seed) {\n")
|
||||
emith(" %xi = ashr i32 %x, 16\n %yi = ashr i32 %y, 16\n %xf = and i32 %x, 65535\n %yf = and i32 %y, 65535\n")
|
||||
emith(" %xf1 = sub i32 %xf, 65536\n %yf1 = sub i32 %yf, 65536\n")
|
||||
emith(" %u = call i32 @fn_noise_fade(i32 %xf)\n %v = call i32 @fn_noise_fade(i32 %yf)\n")
|
||||
emith(" %u = call i32 @lp_noise_fade(i32 %xf)\n %v = call i32 @lp_noise_fade(i32 %yf)\n")
|
||||
emith(" %xi1 = add i32 %xi, 1\n %yi1 = add i32 %yi, 1\n")
|
||||
emith(" %h00 = call i32 @fn_noise_hash(i32 %seed, i32 %xi, i32 %yi)\n %h10 = call i32 @fn_noise_hash(i32 %seed, i32 %xi1, i32 %yi)\n")
|
||||
emith(" %h01 = call i32 @fn_noise_hash(i32 %seed, i32 %xi, i32 %yi1)\n %h11 = call i32 @fn_noise_hash(i32 %seed, i32 %xi1, i32 %yi1)\n")
|
||||
emith(" %g00 = call i32 @fn_noise_grad2(i32 %h00, i32 %xf, i32 %yf)\n %g10 = call i32 @fn_noise_grad2(i32 %h10, i32 %xf1, i32 %yf)\n")
|
||||
emith(" %g01 = call i32 @fn_noise_grad2(i32 %h01, i32 %xf, i32 %yf1)\n %g11 = call i32 @fn_noise_grad2(i32 %h11, i32 %xf1, i32 %yf1)\n")
|
||||
emith(" %a = call i32 @fn_nfx_lerp(i32 %g00, i32 %g10, i32 %u)\n %b = call i32 @fn_nfx_lerp(i32 %g01, i32 %g11, i32 %u)\n")
|
||||
emith(" %n = call i32 @fn_nfx_lerp(i32 %a, i32 %b, i32 %v)\n %sc = call i32 @fn_nfx_mul(i32 %n, i32 92682)\n %r = call i32 @fn_noise_clamp(i32 %sc)\n ret i32 %r\n}\n")
|
||||
emith(" %h00 = call i32 @lp_noise_hash(i32 %seed, i32 %xi, i32 %yi)\n %h10 = call i32 @lp_noise_hash(i32 %seed, i32 %xi1, i32 %yi)\n")
|
||||
emith(" %h01 = call i32 @lp_noise_hash(i32 %seed, i32 %xi, i32 %yi1)\n %h11 = call i32 @lp_noise_hash(i32 %seed, i32 %xi1, i32 %yi1)\n")
|
||||
emith(" %g00 = call i32 @lp_noise_grad2(i32 %h00, i32 %xf, i32 %yf)\n %g10 = call i32 @lp_noise_grad2(i32 %h10, i32 %xf1, i32 %yf)\n")
|
||||
emith(" %g01 = call i32 @lp_noise_grad2(i32 %h01, i32 %xf, i32 %yf1)\n %g11 = call i32 @lp_noise_grad2(i32 %h11, i32 %xf1, i32 %yf1)\n")
|
||||
emith(" %a = call i32 @lp_nfx_lerp(i32 %g00, i32 %g10, i32 %u)\n %b = call i32 @lp_nfx_lerp(i32 %g01, i32 %g11, i32 %u)\n")
|
||||
emith(" %n = call i32 @lp_nfx_lerp(i32 %a, i32 %b, i32 %v)\n %sc = call i32 @lp_nfx_mul(i32 %n, i32 92682)\n %r = call i32 @lp_noise_clamp(i32 %sc)\n ret i32 %r\n}\n")
|
||||
|
||||
# one simplex corner contribution: t = 0.5 - x^2 - y^2; if t <= 0 -> 0, else
|
||||
# t^4 * grad(hash, x, y). Kept as a helper so simplex2 reads as three corners.
|
||||
emith("define i32 @fn_noise_scorner(i32 %hash, i32 %dx, i32 %dy) {\n")
|
||||
emith(" %xx = call i32 @fn_nfx_mul(i32 %dx, i32 %dx)\n %yy = call i32 @fn_nfx_mul(i32 %dy, i32 %dy)\n")
|
||||
emith("define i32 @lp_noise_scorner(i32 %hash, i32 %dx, i32 %dy) {\n")
|
||||
emith(" %xx = call i32 @lp_nfx_mul(i32 %dx, i32 %dx)\n %yy = call i32 @lp_nfx_mul(i32 %dy, i32 %dy)\n")
|
||||
emith(" %s0 = sub i32 32768, %xx\n %t = sub i32 %s0, %yy\n %neg = icmp sle i32 %t, 0\n br i1 %neg, label %zero, label %go\n")
|
||||
emith("zero:\n ret i32 0\n")
|
||||
emith("go:\n %t2 = call i32 @fn_nfx_mul(i32 %t, i32 %t)\n %t4 = call i32 @fn_nfx_mul(i32 %t2, i32 %t2)\n")
|
||||
emith(" %g = call i32 @fn_noise_grad2(i32 %hash, i32 %dx, i32 %dy)\n %r = call i32 @fn_nfx_mul(i32 %t4, i32 %g)\n ret i32 %r\n}\n")
|
||||
emith("go:\n %t2 = call i32 @lp_nfx_mul(i32 %t, i32 %t)\n %t4 = call i32 @lp_nfx_mul(i32 %t2, i32 %t2)\n")
|
||||
emith(" %g = call i32 @lp_noise_grad2(i32 %hash, i32 %dx, i32 %dy)\n %r = call i32 @lp_nfx_mul(i32 %t4, i32 %g)\n ret i32 %r\n}\n")
|
||||
|
||||
# 2D simplex noise (skewed triangular lattice). F2 = (sqrt3-1)/2 = 23994,
|
||||
# G2 = (3-sqrt3)/6 = 13849 in Q16.16. Sum of three corner contributions, scaled
|
||||
# into [-1,1] and clamped.
|
||||
emith("define i32 @fn_noise_simplex2(i32 %x, i32 %y, i32 %seed) {\n")
|
||||
emith(" %sum0 = add i32 %x, %y\n %skew = call i32 @fn_nfx_mul(i32 %sum0, i32 23994)\n")
|
||||
emith("define i32 @lp_noise_simplex2(i32 %x, i32 %y, i32 %seed) {\n")
|
||||
emith(" %sum0 = add i32 %x, %y\n %skew = call i32 @lp_nfx_mul(i32 %sum0, i32 23994)\n")
|
||||
emith(" %xs = add i32 %x, %skew\n %ys = add i32 %y, %skew\n %i = ashr i32 %xs, 16\n %j = ashr i32 %ys, 16\n")
|
||||
emith(" %ij = add i32 %i, %j\n %tt = mul i32 %ij, 13849\n") # t = (i+j)*G2, fixed
|
||||
emith(" %if0 = shl i32 %i, 16\n %jf0 = shl i32 %j, 16\n")
|
||||
|
|
@ -153,30 +153,30 @@ function emit_noise_prelude() -> void {
|
|||
emith(" %x1a = sub i32 %x0, %i1f\n %x1 = add i32 %x1a, 13849\n %y1a = sub i32 %y0, %j1f\n %y1 = add i32 %y1a, 13849\n")
|
||||
emith(" %x2a = sub i32 %x0, 65536\n %x2 = add i32 %x2a, 27698\n %y2a = sub i32 %y0, 65536\n %y2 = add i32 %y2a, 27698\n")
|
||||
emith(" %i1p = add i32 %i, %i1\n %j1p = add i32 %j, %j1\n %i2 = add i32 %i, 1\n %j2 = add i32 %j, 1\n")
|
||||
emith(" %gi0 = call i32 @fn_noise_hash(i32 %seed, i32 %i, i32 %j)\n %gi1 = call i32 @fn_noise_hash(i32 %seed, i32 %i1p, i32 %j1p)\n %gi2 = call i32 @fn_noise_hash(i32 %seed, i32 %i2, i32 %j2)\n")
|
||||
emith(" %n0 = call i32 @fn_noise_scorner(i32 %gi0, i32 %x0, i32 %y0)\n %n1 = call i32 @fn_noise_scorner(i32 %gi1, i32 %x1, i32 %y1)\n %n2 = call i32 @fn_noise_scorner(i32 %gi2, i32 %x2, i32 %y2)\n")
|
||||
emith(" %gi0 = call i32 @lp_noise_hash(i32 %seed, i32 %i, i32 %j)\n %gi1 = call i32 @lp_noise_hash(i32 %seed, i32 %i1p, i32 %j1p)\n %gi2 = call i32 @lp_noise_hash(i32 %seed, i32 %i2, i32 %j2)\n")
|
||||
emith(" %n0 = call i32 @lp_noise_scorner(i32 %gi0, i32 %x0, i32 %y0)\n %n1 = call i32 @lp_noise_scorner(i32 %gi1, i32 %x1, i32 %y1)\n %n2 = call i32 @lp_noise_scorner(i32 %gi2, i32 %x2, i32 %y2)\n")
|
||||
emith(" %sa = add i32 %n0, %n1\n %sb = add i32 %sa, %n2\n")
|
||||
emith(" %sc = mul i32 %sb, 45\n %r = call i32 @fn_noise_clamp(i32 %sc)\n ret i32 %r\n}\n")
|
||||
emith(" %sc = mul i32 %sb, 45\n %r = call i32 @lp_noise_clamp(i32 %sc)\n ret i32 %r\n}\n")
|
||||
|
||||
# fractal Brownian motion: sum `oct` octaves of simplex at rising frequency
|
||||
# (lacunarity 2.0) and falling amplitude (gain 0.5), normalised by total
|
||||
# amplitude so the result stays in [-1,1]. seed varies per octave.
|
||||
emith("define i32 @fn_noise_fbm2(i32 %x, i32 %y, i32 %seed, i32 %oct) {\n")
|
||||
emith("define i32 @lp_noise_fbm2(i32 %x, i32 %y, i32 %seed, i32 %oct) {\n")
|
||||
emith("entry:\n %sump = alloca i32\n %normp = alloca i32\n %ampp = alloca i32\n %freqp = alloca i32\n %op = alloca i32\n")
|
||||
emith(" store i32 0, ptr %sump\n store i32 0, ptr %normp\n store i32 65536, ptr %ampp\n store i32 65536, ptr %freqp\n store i32 0, ptr %op\n br label %cond\n")
|
||||
emith("cond:\n %o = load i32, ptr %op\n %lt = icmp slt i32 %o, %oct\n br i1 %lt, label %body, label %done\n")
|
||||
emith("body:\n %freq = load i32, ptr %freqp\n %amp = load i32, ptr %ampp\n")
|
||||
emith(" %fx = call i32 @fn_nfx_mul(i32 %x, i32 %freq)\n %fy = call i32 @fn_nfx_mul(i32 %y, i32 %freq)\n")
|
||||
emith(" %so = add i32 %seed, %o\n %n = call i32 @fn_noise_simplex2(i32 %fx, i32 %fy, i32 %so)\n")
|
||||
emith(" %na = call i32 @fn_nfx_mul(i32 %n, i32 %amp)\n %sum = load i32, ptr %sump\n %sum2 = add i32 %sum, %na\n store i32 %sum2, ptr %sump\n")
|
||||
emith(" %fx = call i32 @lp_nfx_mul(i32 %x, i32 %freq)\n %fy = call i32 @lp_nfx_mul(i32 %y, i32 %freq)\n")
|
||||
emith(" %so = add i32 %seed, %o\n %n = call i32 @lp_noise_simplex2(i32 %fx, i32 %fy, i32 %so)\n")
|
||||
emith(" %na = call i32 @lp_nfx_mul(i32 %n, i32 %amp)\n %sum = load i32, ptr %sump\n %sum2 = add i32 %sum, %na\n store i32 %sum2, ptr %sump\n")
|
||||
emith(" %norm = load i32, ptr %normp\n %norm2 = add i32 %norm, %amp\n store i32 %norm2, ptr %normp\n")
|
||||
emith(" %amp2 = call i32 @fn_nfx_mul(i32 %amp, i32 32768)\n store i32 %amp2, ptr %ampp\n")
|
||||
emith(" %freq2 = call i32 @fn_nfx_mul(i32 %freq, i32 131072)\n store i32 %freq2, ptr %freqp\n")
|
||||
emith(" %amp2 = call i32 @lp_nfx_mul(i32 %amp, i32 32768)\n store i32 %amp2, ptr %ampp\n")
|
||||
emith(" %freq2 = call i32 @lp_nfx_mul(i32 %freq, i32 131072)\n store i32 %freq2, ptr %freqp\n")
|
||||
emith(" %o1 = add i32 %o, 1\n store i32 %o1, ptr %op\n br label %cond\n")
|
||||
emith("done:\n %fsum = load i32, ptr %sump\n %fnorm = load i32, ptr %normp\n")
|
||||
emith(" %nz = icmp eq i32 %fnorm, 0\n br i1 %nz, label %z, label %div\n")
|
||||
emith("z:\n ret i32 0\n")
|
||||
emith("div:\n %d = call i32 @fn_nfx_div(i32 %fsum, i32 %fnorm)\n %r = call i32 @fn_noise_clamp(i32 %d)\n ret i32 %r\n}\n")
|
||||
emith("div:\n %d = call i32 @lp_nfx_div(i32 %fsum, i32 %fnorm)\n %r = call i32 @lp_noise_clamp(i32 %d)\n ret i32 %r\n}\n")
|
||||
|
||||
# Worley / cellular noise: scan the 3x3 neighbourhood of cells, each holding one
|
||||
# feature point placed by its cell hash, and return the distance to (and id of)
|
||||
|
|
@ -188,7 +188,7 @@ function emit_noise_prelude() -> void {
|
|||
function emit_noise_cellular() -> void {
|
||||
# core scan -> writes the min squared distance to %d2out and the winning id to
|
||||
# %idout (both caller-allocated), so both public entry points share one loop.
|
||||
emith("define void @fn_noise_cell_scan(i32 %x, i32 %y, i32 %seed, ptr %d2out, ptr %idout) {\n")
|
||||
emith("define void @lp_noise_cell_scan(i32 %x, i32 %y, i32 %seed, ptr %d2out, ptr %idout) {\n")
|
||||
emith("entry:\n %xi = ashr i32 %x, 16\n %yi = ashr i32 %y, 16\n")
|
||||
emith(" %bestp = alloca i32\n %idp = alloca i32\n %dyp = alloca i32\n %dxp = alloca i32\n")
|
||||
emith(" store i32 2147483647, ptr %bestp\n store i32 0, ptr %idp\n store i32 -1, ptr %dyp\n br label %yc\n")
|
||||
|
|
@ -196,22 +196,22 @@ function emit_noise_cellular() -> void {
|
|||
emith("yb:\n store i32 -1, ptr %dxp\n br label %xc\n")
|
||||
emith("xc:\n %ox = load i32, ptr %dxp\n %xok = icmp sle i32 %ox, 1\n br i1 %xok, label %xb, label %xdone\n")
|
||||
emith("xb:\n %cx = add i32 %xi, %ox\n %cy = add i32 %yi, %oy\n")
|
||||
emith(" %h = call i32 @fn_noise_hash(i32 %seed, i32 %cx, i32 %cy)\n")
|
||||
emith(" %h = call i32 @lp_noise_hash(i32 %seed, i32 %cx, i32 %cy)\n")
|
||||
emith(" %fxr = and i32 %h, 65535\n %hs = lshr i32 %h, 16\n %fyr = and i32 %hs, 65535\n")
|
||||
emith(" %cxf = shl i32 %cx, 16\n %cyf = shl i32 %cy, 16\n %pxr = add i32 %cxf, %fxr\n %pyr = add i32 %cyf, %fyr\n")
|
||||
emith(" %ddx = sub i32 %pxr, %x\n %ddy = sub i32 %pyr, %y\n")
|
||||
emith(" %dxx = call i32 @fn_nfx_mul(i32 %ddx, i32 %ddx)\n %dyy = call i32 @fn_nfx_mul(i32 %ddy, i32 %ddy)\n %d2 = add i32 %dxx, %dyy\n")
|
||||
emith(" %dxx = call i32 @lp_nfx_mul(i32 %ddx, i32 %ddx)\n %dyy = call i32 @lp_nfx_mul(i32 %ddy, i32 %ddy)\n %d2 = add i32 %dxx, %dyy\n")
|
||||
emith(" %best = load i32, ptr %bestp\n %less = icmp slt i32 %d2, %best\n br i1 %less, label %upd, label %skip\n")
|
||||
emith("upd:\n store i32 %d2, ptr %bestp\n store i32 %h, ptr %idp\n br label %skip\n")
|
||||
emith("skip:\n %ox1 = add i32 %ox, 1\n store i32 %ox1, ptr %dxp\n br label %xc\n")
|
||||
emith("xdone:\n %oy1 = add i32 %oy, 1\n store i32 %oy1, ptr %dyp\n br label %yc\n")
|
||||
emith("ydone:\n %fb = load i32, ptr %bestp\n store i32 %fb, ptr %d2out\n %fi = load i32, ptr %idp\n store i32 %fi, ptr %idout\n ret void\n}\n")
|
||||
|
||||
emith("define i32 @fn_noise_cellular2(i32 %x, i32 %y, i32 %seed) {\n")
|
||||
emith(" %d2 = alloca i32\n %id = alloca i32\n call void @fn_noise_cell_scan(i32 %x, i32 %y, i32 %seed, ptr %d2, ptr %id)\n")
|
||||
emith(" %v = load i32, ptr %d2\n %r = call i32 @fn_fx_sqrt(i32 %v)\n ret i32 %r\n}\n")
|
||||
emith("define i32 @lp_noise_cellular2(i32 %x, i32 %y, i32 %seed) {\n")
|
||||
emith(" %d2 = alloca i32\n %id = alloca i32\n call void @lp_noise_cell_scan(i32 %x, i32 %y, i32 %seed, ptr %d2, ptr %id)\n")
|
||||
emith(" %v = load i32, ptr %d2\n %r = call i32 @lp_fx_sqrt(i32 %v)\n ret i32 %r\n}\n")
|
||||
|
||||
emith("define i32 @fn_noise_cellular2_id(i32 %x, i32 %y, i32 %seed) {\n")
|
||||
emith(" %d2 = alloca i32\n %id = alloca i32\n call void @fn_noise_cell_scan(i32 %x, i32 %y, i32 %seed, ptr %d2, ptr %id)\n")
|
||||
emith("define i32 @lp_noise_cellular2_id(i32 %x, i32 %y, i32 %seed) {\n")
|
||||
emith(" %d2 = alloca i32\n %id = alloca i32\n call void @lp_noise_cell_scan(i32 %x, i32 %y, i32 %seed, ptr %d2, ptr %id)\n")
|
||||
emith(" %r = load i32, ptr %id\n ret i32 %r\n}\n")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
# Os.set_env(name, val) -> bool set it (true on success)
|
||||
# Os.unset_env(name) -> bool remove it (true on success)
|
||||
# Os.exit(code) terminate the process with a status code
|
||||
# Os.pid() -> int this process's id (unique among live processes)
|
||||
# Os.platform() -> string "macos" | "linux" | ...(raw uname sysname)
|
||||
# Os.arch() -> string machine arch, e.g. "arm64" | "x86_64"
|
||||
# Os.stdout_write(s) write a string to standard output
|
||||
|
|
@ -35,15 +36,15 @@ function is_os_ns(meth: pointer) -> bool {
|
|||
if (meth == "args") or (meth == "arg_count") or (meth == "arg") { return true }
|
||||
if (meth == "env") or (meth == "env_or") or (meth == "has_env") { return true }
|
||||
if (meth == "set_env") or (meth == "unset_env") { return true }
|
||||
if (meth == "exit") or (meth == "platform") or (meth == "arch") { return true }
|
||||
if (meth == "exit") or (meth == "pid") or (meth == "platform") or (meth == "arch") { return true }
|
||||
if (meth == "stdout_write") or (meth == "stderr_write") { return true }
|
||||
if (meth == "save_dir") or (meth == "config_dir") or (meth == "cache_dir") or (meth == "temp_dir") { return true }
|
||||
return false
|
||||
}
|
||||
|
||||
function emit_os_ns(meth: pointer, e: Node) -> Val {
|
||||
# arg_count / arg / exit stay light — they mirror the bare intrinsics and need
|
||||
# no Os runtime prelude, so a program using only these emits no extra IR.
|
||||
# arg_count / arg / exit / pid stay light — they mirror the bare intrinsics and
|
||||
# need no Os runtime prelude, so a program using only these emits no extra IR.
|
||||
if (meth == "arg_count") { return val(emit_bind("load i32, ptr @L_argc"), "int") }
|
||||
if (meth == "arg") {
|
||||
let i = emit_expr(e.kids[0])
|
||||
|
|
@ -51,6 +52,7 @@ function emit_os_ns(meth: pointer, e: Node) -> Val {
|
|||
let q = emit_bind(`getelementptr ptr, ptr {v}, i32 {i.code}`)
|
||||
return val(emit_bind(`load ptr, ptr {q}`), "string")
|
||||
}
|
||||
if (meth == "pid") { g_uses_pid = true; return val(emit_bind("call i32 @getpid()"), "int") }
|
||||
if (meth == "exit") {
|
||||
let n = emit_expr(e.kids[0])
|
||||
emit(` call void @exit(i32 {n.code})\n`)
|
||||
|
|
@ -79,10 +81,10 @@ function emit_os_ns(meth: pointer, e: Node) -> Val {
|
|||
}
|
||||
# everything below is served by the Os runtime prelude
|
||||
g_uses_osrt = true
|
||||
if (meth == "args") { return val(emit_bind("call ptr @fn_os_args()"), "[]string") }
|
||||
if (meth == "args") { return val(emit_bind("call ptr @lp_os_args()"), "[]string") }
|
||||
if (meth == "env_or") {
|
||||
let nm = emit_expr(e.kids[0]); let fb = emit_expr(e.kids[1])
|
||||
return val(emit_bind(`call ptr @fn_os_getenv_or(ptr {nm.code}, ptr {fb.code})`), "string")
|
||||
return val(emit_bind(`call ptr @lp_os_getenv_or(ptr {nm.code}, ptr {fb.code})`), "string")
|
||||
}
|
||||
if (meth == "set_env") {
|
||||
let nm = emit_expr(e.kids[0]); let v = emit_expr(e.kids[1])
|
||||
|
|
@ -96,22 +98,22 @@ function emit_os_ns(meth: pointer, e: Node) -> Val {
|
|||
let ok = emit_bind(`icmp eq i32 {r}, 0`)
|
||||
return val(emit_bind(`zext i1 {ok} to i32`), "bool")
|
||||
}
|
||||
if (meth == "platform") { return val(emit_bind("call ptr @fn_os_platform()"), "string") }
|
||||
if (meth == "arch") { return val(emit_bind("call ptr @fn_os_arch()"), "string") }
|
||||
if (meth == "platform") { return val(emit_bind("call ptr @lp_os_platform()"), "string") }
|
||||
if (meth == "arch") { return val(emit_bind("call ptr @lp_os_arch()"), "string") }
|
||||
if (meth == "save_dir") {
|
||||
let a = emit_expr(e.kids[0])
|
||||
return val(emit_bind(`call ptr @fn_os_save_dir(ptr {a.code})`), "string")
|
||||
return val(emit_bind(`call ptr @lp_os_save_dir(ptr {a.code})`), "string")
|
||||
}
|
||||
if (meth == "config_dir") {
|
||||
let a = emit_expr(e.kids[0])
|
||||
return val(emit_bind(`call ptr @fn_os_config_dir(ptr {a.code})`), "string")
|
||||
return val(emit_bind(`call ptr @lp_os_config_dir(ptr {a.code})`), "string")
|
||||
}
|
||||
if (meth == "cache_dir") {
|
||||
let a = emit_expr(e.kids[0])
|
||||
return val(emit_bind(`call ptr @fn_os_cache_dir(ptr {a.code})`), "string")
|
||||
return val(emit_bind(`call ptr @lp_os_cache_dir(ptr {a.code})`), "string")
|
||||
}
|
||||
# temp_dir
|
||||
return val(emit_bind("call ptr @fn_os_temp_dir()"), "string")
|
||||
return val(emit_bind("call ptr @lp_os_temp_dir()"), "string")
|
||||
}
|
||||
|
||||
# emit_os_prelude — the Os runtime, emitted once per program that uses the
|
||||
|
|
@ -135,34 +137,34 @@ function emit_os_prelude() -> void {
|
|||
let k_linux = emit_str_const("linux")
|
||||
|
||||
# getenv(name) or a fallback when it is unset
|
||||
emith("define ptr @fn_os_getenv_or(ptr %name, ptr %fb) {\n")
|
||||
emith("define ptr @lp_os_getenv_or(ptr %name, ptr %fb) {\n")
|
||||
emith("entry:\n %r = call ptr @getenv(ptr %name)\n %z = icmp eq ptr %r, null\n br i1 %z, label %use, label %got\n")
|
||||
emith("use:\n ret ptr %fb\n")
|
||||
emith("got:\n ret ptr %r\n}\n")
|
||||
|
||||
# concatenate two NUL-terminated strings into a fresh malloc'd buffer
|
||||
emith("define ptr @fn_os_join2(ptr %a, ptr %b) {\n")
|
||||
emith("define ptr @lp_os_join2(ptr %a, ptr %b) {\n")
|
||||
emith("entry:\n %la = call i64 @strlen(ptr %a)\n %lb = call i64 @strlen(ptr %b)\n")
|
||||
emith(" %sum = add i64 %la, %lb\n %tot = add i64 %sum, 1\n %m = call ptr @malloc(i64 %tot)\n")
|
||||
emith(" call ptr @memcpy(ptr %m, ptr %a, i64 %la)\n")
|
||||
emith(" %m2 = getelementptr i8, ptr %m, i64 %la\n call ptr @memcpy(ptr %m2, ptr %b, i64 %lb)\n")
|
||||
emith(" %end = getelementptr i8, ptr %m, i64 %sum\n store i8 0, ptr %end\n ret ptr %m\n}\n")
|
||||
|
||||
emith("define ptr @fn_os_join3(ptr %a, ptr %b, ptr %c) {\n")
|
||||
emith("entry:\n %ab = call ptr @fn_os_join2(ptr %a, ptr %b)\n %r = call ptr @fn_os_join2(ptr %ab, ptr %c)\n ret ptr %r\n}\n")
|
||||
emith("define ptr @lp_os_join3(ptr %a, ptr %b, ptr %c) {\n")
|
||||
emith("entry:\n %ab = call ptr @lp_os_join2(ptr %a, ptr %b)\n %r = call ptr @lp_os_join2(ptr %ab, ptr %c)\n ret ptr %r\n}\n")
|
||||
|
||||
# the user's home directory, or "." when HOME is unset
|
||||
emith(`define ptr @fn_os_home() {{\n %r = call ptr @fn_os_getenv_or(ptr {k_home}, ptr {k_dot})\n ret ptr %r\n}}\n`)
|
||||
emith(`define ptr @lp_os_home() {{\n %r = call ptr @lp_os_getenv_or(ptr {k_home}, ptr {k_dot})\n ret ptr %r\n}}\n`)
|
||||
|
||||
# per-user known folders (macOS/BSD layout)
|
||||
emith(`define ptr @fn_os_save_dir(ptr %app) {{\n %h = call ptr @fn_os_home()\n %r = call ptr @fn_os_join3(ptr %h, ptr {k_appsp}, ptr %app)\n ret ptr %r\n}}\n`)
|
||||
emith(`define ptr @fn_os_config_dir(ptr %app) {{\n %h = call ptr @fn_os_home()\n %r = call ptr @fn_os_join3(ptr %h, ptr {k_appsp}, ptr %app)\n ret ptr %r\n}}\n`)
|
||||
emith(`define ptr @fn_os_cache_dir(ptr %app) {{\n %h = call ptr @fn_os_home()\n %r = call ptr @fn_os_join3(ptr %h, ptr {k_cache}, ptr %app)\n ret ptr %r\n}}\n`)
|
||||
emith(`define ptr @fn_os_temp_dir() {{\n %r = call ptr @fn_os_getenv_or(ptr {k_tmpk}, ptr {k_tmp})\n ret ptr %r\n}}\n`)
|
||||
emith(`define ptr @lp_os_save_dir(ptr %app) {{\n %h = call ptr @lp_os_home()\n %r = call ptr @lp_os_join3(ptr %h, ptr {k_appsp}, ptr %app)\n ret ptr %r\n}}\n`)
|
||||
emith(`define ptr @lp_os_config_dir(ptr %app) {{\n %h = call ptr @lp_os_home()\n %r = call ptr @lp_os_join3(ptr %h, ptr {k_appsp}, ptr %app)\n ret ptr %r\n}}\n`)
|
||||
emith(`define ptr @lp_os_cache_dir(ptr %app) {{\n %h = call ptr @lp_os_home()\n %r = call ptr @lp_os_join3(ptr %h, ptr {k_cache}, ptr %app)\n ret ptr %r\n}}\n`)
|
||||
emith(`define ptr @lp_os_temp_dir() {{\n %r = call ptr @lp_os_getenv_or(ptr {k_tmpk}, ptr {k_tmp})\n ret ptr %r\n}}\n`)
|
||||
|
||||
# Os.args() -> a %LSlice of the argv strings (data = argv, len = cap = argc), a
|
||||
# snapshot the caller may iterate or index like any other []string.
|
||||
emith("define ptr @fn_os_args() {\n")
|
||||
emith("define ptr @lp_os_args() {\n")
|
||||
emith("entry:\n %c = load i32, ptr @L_argc\n %v = load ptr, ptr @L_argv\n")
|
||||
emith(" %h = call ptr @malloc(i64 16)\n")
|
||||
emith(" %d0 = getelementptr inbounds %LSlice, ptr %h, i32 0, i32 0\n store ptr %v, ptr %d0\n")
|
||||
|
|
@ -171,7 +173,7 @@ function emit_os_prelude() -> void {
|
|||
emith(" ret ptr %h\n}\n")
|
||||
|
||||
# platform(): uname sysname (field 0, portable) mapped to a short id
|
||||
emith("define ptr @fn_os_platform() {\n")
|
||||
emith("define ptr @lp_os_platform() {\n")
|
||||
emith("entry:\n %buf = call ptr @malloc(i64 8192)\n call i32 @uname(ptr %buf)\n")
|
||||
emith(` %cd = call i32 @strncmp(ptr %buf, ptr {k_darw}, i64 6)\n %isd = icmp eq i32 %cd, 0\n br i1 %isd, label %mac, label %chkl\n`)
|
||||
emith(`mac:\n ret ptr {k_macos}\n`)
|
||||
|
|
@ -182,7 +184,7 @@ function emit_os_prelude() -> void {
|
|||
# arch(): the uname `machine` field. On macOS/BSD utsname each field is 256
|
||||
# bytes, so `machine` (index 4) sits at offset 1024. Documented BSD-layout
|
||||
# assumption (see the header note); other layouts are a follow-up.
|
||||
emith("define ptr @fn_os_arch() {\n")
|
||||
emith("define ptr @lp_os_arch() {\n")
|
||||
emith("entry:\n %buf = call ptr @malloc(i64 8192)\n call i32 @uname(ptr %buf)\n")
|
||||
emith(" %m = getelementptr i8, ptr %buf, i64 1024\n ret ptr %m\n}\n")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,22 +18,22 @@ function emit_text_ns(meth: pointer, e: Node) -> Val {
|
|||
if (meth == "from_int") { # int -> string, same as string(n)
|
||||
let n = emit_expr(e.kids[0])
|
||||
g_uses_intstr = true
|
||||
return val(emit_bind(`call ptr @fn_int_str(i32 {n.code})`), "string")
|
||||
return val(emit_bind(`call ptr @lp_int_str(i32 {n.code})`), "string")
|
||||
}
|
||||
if (meth == "slice") { # s[a..b], same substring helper
|
||||
let s0 = emit_expr(e.kids[0]); let a = emit_expr(e.kids[1]); let b = emit_expr(e.kids[2])
|
||||
g_uses_strslice = true
|
||||
return val(emit_bind(`call ptr @fn_str_slice(ptr {s0.code}, i32 {a.code}, i32 {b.code})`), "string")
|
||||
return val(emit_bind(`call ptr @lp_str_slice(ptr {s0.code}, i32 {a.code}, i32 {b.code})`), "string")
|
||||
}
|
||||
if (meth == "equals") { # byte-wise equality, same as ==
|
||||
let a = emit_expr(e.kids[0]); let b = emit_expr(e.kids[1])
|
||||
g_uses_str = true
|
||||
return val(emit_bind(`call i32 @fn_str_eq(ptr {a.code}, ptr {b.code})`), "bool")
|
||||
return val(emit_bind(`call i32 @lp_str_eq(ptr {a.code}, ptr {b.code})`), "bool")
|
||||
}
|
||||
if (meth == "concat") { # a + b, same as the + operator
|
||||
let a = emit_expr(e.kids[0]); let b = emit_expr(e.kids[1])
|
||||
g_uses_str = true
|
||||
return val(emit_bind(`call ptr @fn_str_concat(ptr {a.code}, ptr {b.code})`), "string")
|
||||
return val(emit_bind(`call ptr @lp_str_concat(ptr {a.code}, ptr {b.code})`), "string")
|
||||
}
|
||||
|
||||
let s = emit_expr(e.kids[0])
|
||||
|
|
@ -52,45 +52,45 @@ function emit_text_ns(meth: pointer, e: Node) -> Val {
|
|||
}
|
||||
if (meth == "upper") { # ASCII a-z -> A-Z, fresh string
|
||||
g_uses_textrt = true
|
||||
return val(emit_bind(`call ptr @fn_str_upper(ptr {s.code})`), "string")
|
||||
return val(emit_bind(`call ptr @lp_str_upper(ptr {s.code})`), "string")
|
||||
}
|
||||
if (meth == "lower") { # ASCII A-Z -> a-z, fresh string
|
||||
g_uses_textrt = true
|
||||
return val(emit_bind(`call ptr @fn_str_lower(ptr {s.code})`), "string")
|
||||
return val(emit_bind(`call ptr @lp_str_lower(ptr {s.code})`), "string")
|
||||
}
|
||||
if (meth == "trim") { # drop leading/trailing whitespace
|
||||
g_uses_textrt = true
|
||||
return val(emit_bind(`call ptr @fn_str_trim(ptr {s.code})`), "string")
|
||||
return val(emit_bind(`call ptr @lp_str_trim(ptr {s.code})`), "string")
|
||||
}
|
||||
if (meth == "repeat") { # s repeated n times
|
||||
g_uses_textrt = true
|
||||
let n = emit_expr(e.kids[1])
|
||||
return val(emit_bind(`call ptr @fn_str_repeat(ptr {s.code}, i32 {n.code})`), "string")
|
||||
return val(emit_bind(`call ptr @lp_str_repeat(ptr {s.code}, i32 {n.code})`), "string")
|
||||
}
|
||||
if (meth == "pad_left") { # pad with spaces to width, on the left
|
||||
g_uses_textrt = true
|
||||
let w = emit_expr(e.kids[1])
|
||||
return val(emit_bind(`call ptr @fn_str_pad(ptr {s.code}, i32 {w.code}, i1 1)`), "string")
|
||||
return val(emit_bind(`call ptr @lp_str_pad(ptr {s.code}, i32 {w.code}, i1 1)`), "string")
|
||||
}
|
||||
if (meth == "pad_right") { # pad with spaces to width, on the right
|
||||
g_uses_textrt = true
|
||||
let w = emit_expr(e.kids[1])
|
||||
return val(emit_bind(`call ptr @fn_str_pad(ptr {s.code}, i32 {w.code}, i1 0)`), "string")
|
||||
return val(emit_bind(`call ptr @lp_str_pad(ptr {s.code}, i32 {w.code}, i1 0)`), "string")
|
||||
}
|
||||
if (meth == "replace") { # replace every `from` with `to`
|
||||
g_uses_textrt2 = true
|
||||
let from = emit_expr(e.kids[1]); let to = emit_expr(e.kids[2])
|
||||
return val(emit_bind(`call ptr @fn_str_replace(ptr {s.code}, ptr {from.code}, ptr {to.code})`), "string")
|
||||
return val(emit_bind(`call ptr @lp_str_replace(ptr {s.code}, ptr {from.code}, ptr {to.code})`), "string")
|
||||
}
|
||||
if (meth == "split") { # split on a separator -> []string
|
||||
g_uses_textrt2 = true
|
||||
let sep = emit_expr(e.kids[1])
|
||||
return val(emit_bind(`call ptr @fn_str_split(ptr {s.code}, ptr {sep.code})`), "[]string")
|
||||
return val(emit_bind(`call ptr @lp_str_split(ptr {s.code}, ptr {sep.code})`), "[]string")
|
||||
}
|
||||
if (meth == "join") { # join a []string with a separator (s is the slice)
|
||||
g_uses_textrt2 = true
|
||||
let sep = emit_expr(e.kids[1])
|
||||
return val(emit_bind(`call ptr @fn_str_join(ptr {s.code}, ptr {sep.code})`), "string")
|
||||
return val(emit_bind(`call ptr @lp_str_join(ptr {s.code}, ptr {sep.code})`), "string")
|
||||
}
|
||||
if (meth == "contains") or (meth == "index_of") { # substring search
|
||||
let sub = emit_expr(e.kids[1])
|
||||
|
|
@ -134,4 +134,3 @@ function emit_text_ns(meth: pointer, e: Node) -> Val {
|
|||
emit(en); emit(":\n")
|
||||
return val(emit_bind(`load i32, ptr {res}`), "bool")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
# repeat/pad. Emitted once per program that uses them (g_uses_textrt). Plain
|
||||
# libc (strlen/malloc/memcpy), deterministic, C-string in and out.
|
||||
function emit_text_prelude() -> void {
|
||||
emith("define ptr @fn_str_upper(ptr %s) {\n")
|
||||
emith("define ptr @lp_str_upper(ptr %s) {\n")
|
||||
emith("entry:\n")
|
||||
emith(" %n = call i64 @strlen(ptr %s)\n")
|
||||
emith(" %sz = add i64 %n, 1\n")
|
||||
|
|
@ -33,7 +33,7 @@ function emit_text_prelude() -> void {
|
|||
emith(" store i8 0, ptr %tp\n")
|
||||
emith(" ret ptr %out\n")
|
||||
emith("}\n")
|
||||
emith("define ptr @fn_str_lower(ptr %s) {\n")
|
||||
emith("define ptr @lp_str_lower(ptr %s) {\n")
|
||||
emith("entry:\n")
|
||||
emith(" %n = call i64 @strlen(ptr %s)\n")
|
||||
emith(" %sz = add i64 %n, 1\n")
|
||||
|
|
@ -63,7 +63,7 @@ function emit_text_prelude() -> void {
|
|||
emith(" store i8 0, ptr %tp\n")
|
||||
emith(" ret ptr %out\n")
|
||||
emith("}\n")
|
||||
emith("define ptr @fn_str_trim(ptr %s) {\n")
|
||||
emith("define ptr @lp_str_trim(ptr %s) {\n")
|
||||
emith("entry:\n")
|
||||
emith(" %n = call i64 @strlen(ptr %s)\n")
|
||||
emith(" %sp = alloca i64\n")
|
||||
|
|
@ -79,7 +79,7 @@ function emit_text_prelude() -> void {
|
|||
emith("lchk:\n")
|
||||
emith(" %p0 = getelementptr i8, ptr %s, i64 %a0\n")
|
||||
emith(" %c0 = load i8, ptr %p0\n")
|
||||
emith(" %ws0 = call i1 @fn_is_ws(i8 %c0)\n")
|
||||
emith(" %ws0 = call i1 @lp_is_ws(i8 %c0)\n")
|
||||
emith(" br i1 %ws0, label %linc, label %rcond\n")
|
||||
emith("linc:\n")
|
||||
emith(" %a1 = add i64 %a0, 1\n")
|
||||
|
|
@ -94,7 +94,7 @@ function emit_text_prelude() -> void {
|
|||
emith(" %em1 = sub i64 %e2, 1\n")
|
||||
emith(" %p1 = getelementptr i8, ptr %s, i64 %em1\n")
|
||||
emith(" %c1 = load i8, ptr %p1\n")
|
||||
emith(" %ws1 = call i1 @fn_is_ws(i8 %c1)\n")
|
||||
emith(" %ws1 = call i1 @lp_is_ws(i8 %c1)\n")
|
||||
emith(" br i1 %ws1, label %rdec, label %build\n")
|
||||
emith("rdec:\n")
|
||||
emith(" store i64 %em1, ptr %ep\n")
|
||||
|
|
@ -111,7 +111,7 @@ function emit_text_prelude() -> void {
|
|||
emith(" store i8 0, ptr %tp\n")
|
||||
emith(" ret ptr %out\n")
|
||||
emith("}\n")
|
||||
emith("define i1 @fn_is_ws(i8 %c) {\n")
|
||||
emith("define i1 @lp_is_ws(i8 %c) {\n")
|
||||
emith(" %a = icmp eq i8 %c, 32\n")
|
||||
emith(" %b = icmp eq i8 %c, 9\n")
|
||||
emith(" %d = icmp eq i8 %c, 10\n")
|
||||
|
|
@ -121,7 +121,7 @@ function emit_text_prelude() -> void {
|
|||
emith(" %r = or i1 %ab, %de\n")
|
||||
emith(" ret i1 %r\n")
|
||||
emith("}\n")
|
||||
emith("define ptr @fn_str_repeat(ptr %s, i32 %n32) {\n")
|
||||
emith("define ptr @lp_str_repeat(ptr %s, i32 %n32) {\n")
|
||||
emith("entry:\n")
|
||||
emith(" %nneg = icmp slt i32 %n32, 0\n")
|
||||
emith(" %nn = select i1 %nneg, i32 0, i32 %n32\n")
|
||||
|
|
@ -149,7 +149,7 @@ function emit_text_prelude() -> void {
|
|||
emith(" store i8 0, ptr %tp\n")
|
||||
emith(" ret ptr %out\n")
|
||||
emith("}\n")
|
||||
emith("define ptr @fn_str_pad(ptr %s, i32 %w32, i1 %left) {\n")
|
||||
emith("define ptr @lp_str_pad(ptr %s, i32 %w32, i1 %left) {\n")
|
||||
emith("entry:\n")
|
||||
emith(" %L = call i64 @strlen(ptr %s)\n")
|
||||
emith(" %w0 = sext i32 %w32 to i64\n")
|
||||
|
|
@ -169,20 +169,20 @@ function emit_text_prelude() -> void {
|
|||
emith(" %out = call ptr @malloc(i64 %sz)\n")
|
||||
emith(" br i1 %left, label %padleft, label %padright\n")
|
||||
emith("padleft:\n")
|
||||
emith(" call void @fn_fill_sp(ptr %out, i64 0, i64 %pad)\n")
|
||||
emith(" call void @lp_fill_sp(ptr %out, i64 0, i64 %pad)\n")
|
||||
emith(" %dstL = getelementptr i8, ptr %out, i64 %pad\n")
|
||||
emith(" call ptr @memcpy(ptr %dstL, ptr %s, i64 %L)\n")
|
||||
emith(" br label %term\n")
|
||||
emith("padright:\n")
|
||||
emith(" call ptr @memcpy(ptr %out, ptr %s, i64 %L)\n")
|
||||
emith(" call void @fn_fill_sp(ptr %out, i64 %L, i64 %pad)\n")
|
||||
emith(" call void @lp_fill_sp(ptr %out, i64 %L, i64 %pad)\n")
|
||||
emith(" br label %term\n")
|
||||
emith("term:\n")
|
||||
emith(" %tp = getelementptr i8, ptr %out, i64 %w0\n")
|
||||
emith(" store i8 0, ptr %tp\n")
|
||||
emith(" ret ptr %out\n")
|
||||
emith("}\n")
|
||||
emith("define void @fn_fill_sp(ptr %buf, i64 %start, i64 %count) {\n")
|
||||
emith("define void @lp_fill_sp(ptr %buf, i64 %start, i64 %count) {\n")
|
||||
emith("entry:\n")
|
||||
emith(" %ip = alloca i64\n")
|
||||
emith(" store i64 0, ptr %ip\n")
|
||||
|
|
@ -208,7 +208,7 @@ function emit_text_prelude() -> void {
|
|||
# program that uses them (g_uses_textrt2). Slices are the {data,len,cap}
|
||||
# %LSlice with str (ptr) elements.
|
||||
function emit_text2_prelude() -> void {
|
||||
emith("define ptr @fn_str_replace(ptr %s, ptr %from, ptr %to) {\n")
|
||||
emith("define ptr @lp_str_replace(ptr %s, ptr %from, ptr %to) {\n")
|
||||
emith("entry:\n")
|
||||
emith(" %lf = call i64 @strlen(ptr %from)\n")
|
||||
emith(" %lz = icmp eq i64 %lf, 0\n")
|
||||
|
|
@ -277,7 +277,7 @@ function emit_text2_prelude() -> void {
|
|||
emith(" call ptr @memcpy(ptr %dst3, ptr %src3, i64 %remp1)\n")
|
||||
emith(" ret ptr %out\n")
|
||||
emith("}\n")
|
||||
emith("define ptr @fn_str_join(ptr %h, ptr %sep) {\n")
|
||||
emith("define ptr @lp_str_join(ptr %h, ptr %sep) {\n")
|
||||
emith("entry:\n")
|
||||
emith(" %lp = getelementptr inbounds %LSlice, ptr %h, i32 0, i32 1\n")
|
||||
emith(" %len32 = load i32, ptr %lp\n")
|
||||
|
|
@ -347,7 +347,7 @@ function emit_text2_prelude() -> void {
|
|||
emith(" store i8 0, ptr %dsf\n")
|
||||
emith(" ret ptr %out\n")
|
||||
emith("}\n")
|
||||
emith("define ptr @fn_str_split(ptr %s, ptr %sep) {\n")
|
||||
emith("define ptr @lp_str_split(ptr %s, ptr %sep) {\n")
|
||||
emith("entry:\n")
|
||||
emith(" %lsep = call i64 @strlen(ptr %sep)\n")
|
||||
emith(" %cntp = alloca i64\n")
|
||||
|
|
|
|||
|
|
@ -43,35 +43,35 @@ function emit_unicode_ns(meth: pointer, e: Node) -> Val {
|
|||
}
|
||||
if (meth == "len") {
|
||||
let s = emit_expr(e.kids[0])
|
||||
return val(emit_bind(`call i32 @fn_uni_len(ptr {s.code})`), "int")
|
||||
return val(emit_bind(`call i32 @lp_uni_len(ptr {s.code})`), "int")
|
||||
}
|
||||
if (meth == "is_valid_utf8") {
|
||||
let s = emit_expr(e.kids[0])
|
||||
return val(emit_bind(`call i32 @fn_uni_valid(ptr {s.code})`), "bool")
|
||||
return val(emit_bind(`call i32 @lp_uni_valid(ptr {s.code})`), "bool")
|
||||
}
|
||||
if (meth == "char_at") {
|
||||
let s = emit_expr(e.kids[0]); let i = emit_expr(e.kids[1])
|
||||
return val(emit_bind(`call i32 @fn_uni_char_at(ptr {s.code}, i32 {i.code})`), "int")
|
||||
return val(emit_bind(`call i32 @lp_uni_char_at(ptr {s.code}, i32 {i.code})`), "int")
|
||||
}
|
||||
if (meth == "chars") {
|
||||
let s = emit_expr(e.kids[0])
|
||||
return val(emit_bind(`call ptr @fn_uni_chars(ptr {s.code})`), "[]int")
|
||||
return val(emit_bind(`call ptr @lp_uni_chars(ptr {s.code})`), "[]int")
|
||||
}
|
||||
if (meth == "upper") {
|
||||
let s = emit_expr(e.kids[0])
|
||||
return val(emit_bind(`call ptr @fn_uni_case(ptr {s.code}, i32 1)`), "string")
|
||||
return val(emit_bind(`call ptr @lp_uni_case(ptr {s.code}, i32 1)`), "string")
|
||||
}
|
||||
if (meth == "lower") {
|
||||
let s = emit_expr(e.kids[0])
|
||||
return val(emit_bind(`call ptr @fn_uni_case(ptr {s.code}, i32 0)`), "string")
|
||||
return val(emit_bind(`call ptr @lp_uni_case(ptr {s.code}, i32 0)`), "string")
|
||||
}
|
||||
if (meth == "truncate") {
|
||||
let s = emit_expr(e.kids[0]); let n = emit_expr(e.kids[1])
|
||||
return val(emit_bind(`call ptr @fn_uni_truncate(ptr {s.code}, i32 {n.code})`), "string")
|
||||
return val(emit_bind(`call ptr @lp_uni_truncate(ptr {s.code}, i32 {n.code})`), "string")
|
||||
}
|
||||
# grapheme_len
|
||||
let s = emit_expr(e.kids[0])
|
||||
return val(emit_bind(`call i32 @fn_uni_grapheme_len(ptr {s.code})`), "int")
|
||||
return val(emit_bind(`call i32 @lp_uni_grapheme_len(ptr {s.code})`), "int")
|
||||
}
|
||||
|
||||
# emit_unicode_prelude — the UTF-8 runtime, emitted once per program that uses
|
||||
|
|
@ -87,7 +87,7 @@ function emit_uni_core() -> void {
|
|||
# code-point count: every byte that is NOT a UTF-8 continuation byte
|
||||
# (0b10xxxxxx) begins a new code point. Walks byte-by-byte, so it is safe on
|
||||
# truncated/invalid input and stops exactly at the NUL.
|
||||
emith("define i32 @fn_uni_len(ptr %s) {\n")
|
||||
emith("define i32 @lp_uni_len(ptr %s) {\n")
|
||||
emith("entry:\n %ip = alloca i32\n %np = alloca i32\n store i32 0, ptr %ip\n store i32 0, ptr %np\n br label %lp\n")
|
||||
emith("lp:\n %i = load i32, ptr %ip\n %p = getelementptr i8, ptr %s, i32 %i\n %b = load i8, ptr %p\n %c = zext i8 %b to i32\n")
|
||||
emith(" %z = icmp eq i32 %c, 0\n br i1 %z, label %done, label %go\n")
|
||||
|
|
@ -100,7 +100,7 @@ function emit_uni_core() -> void {
|
|||
# offset just past it. Lenient and overrun-safe: a truncated multibyte sequence
|
||||
# (a continuation byte that is NUL) or an invalid lead byte decodes as a single
|
||||
# byte, so the walk always makes progress and never reads past the terminator.
|
||||
emith("define i32 @fn_uni_decode(ptr %s, i32 %i, ptr %cpout) {\n")
|
||||
emith("define i32 @lp_uni_decode(ptr %s, i32 %i, ptr %cpout) {\n")
|
||||
emith("entry:\n %p0 = getelementptr i8, ptr %s, i32 %i\n %b0 = load i8, ptr %p0\n %c0 = zext i8 %b0 to i32\n")
|
||||
emith(" %a1 = icmp ult i32 %c0, 128\n br i1 %a1, label %one, label %multi\n")
|
||||
emith("one:\n store i32 %c0, ptr %cpout\n %oi = add i32 %i, 1\n ret i32 %oi\n")
|
||||
|
|
@ -135,7 +135,7 @@ function emit_uni_core() -> void {
|
|||
|
||||
# strict UTF-8 validation: correct continuation bytes, no overlong encodings,
|
||||
# no surrogates (U+D800..U+DFFF), and nothing above U+10FFFF. Returns 1/0.
|
||||
emith("define i32 @fn_uni_valid(ptr %s) {\n")
|
||||
emith("define i32 @lp_uni_valid(ptr %s) {\n")
|
||||
emith("entry:\n %ip = alloca i32\n store i32 0, ptr %ip\n br label %lp\n")
|
||||
emith("lp:\n %i = load i32, ptr %ip\n %p = getelementptr i8, ptr %s, i32 %i\n %b = load i8, ptr %p\n %c = zext i8 %b to i32\n")
|
||||
emith(" %z = icmp eq i32 %c, 0\n br i1 %z, label %good, label %g\n")
|
||||
|
|
@ -180,26 +180,26 @@ function emit_uni_core() -> void {
|
|||
function emit_uni_case_fns() -> void {
|
||||
# uppercase one code point: ASCII a-z and Latin-1 a-with-diacritic .. thorn
|
||||
# (0xE0..0xFE except 0xF7). Other code points pass through unchanged (v1).
|
||||
emith("define i32 @fn_uni_upcp(i32 %c) {\n")
|
||||
emith("define i32 @lp_uni_upcp(i32 %c) {\n")
|
||||
emith(" %la = icmp uge i32 %c, 97\n %lb = icmp ule i32 %c, 122\n %asc = and i1 %la, %lb\n")
|
||||
emith(" %da = icmp uge i32 %c, 224\n %db = icmp ule i32 %c, 254\n %dd = icmp ne i32 %c, 247\n %d1 = and i1 %da, %db\n %d2 = and i1 %d1, %dd\n")
|
||||
emith(" %map = or i1 %asc, %d2\n %up = sub i32 %c, 32\n %r = select i1 %map, i32 %up, i32 %c\n ret i32 %r\n}\n")
|
||||
|
||||
# lowercase one code point: ASCII A-Z and Latin-1 A-with-diacritic .. Thorn
|
||||
# (0xC0..0xDE except 0xD7). Other code points pass through unchanged (v1).
|
||||
emith("define i32 @fn_uni_locp(i32 %c) {\n")
|
||||
emith("define i32 @lp_uni_locp(i32 %c) {\n")
|
||||
emith(" %ua = icmp uge i32 %c, 65\n %ub = icmp ule i32 %c, 90\n %asc = and i1 %ua, %ub\n")
|
||||
emith(" %da = icmp uge i32 %c, 192\n %db = icmp ule i32 %c, 222\n %dd = icmp ne i32 %c, 215\n %d1 = and i1 %da, %db\n %d2 = and i1 %d1, %dd\n")
|
||||
emith(" %map = or i1 %asc, %d2\n %lo = add i32 %c, 32\n %r = select i1 %map, i32 %lo, i32 %c\n ret i32 %r\n}\n")
|
||||
|
||||
# UTF-8 byte width needed to encode a code point
|
||||
emith("define i32 @fn_uni_cpwidth(i32 %cp) {\n")
|
||||
emith("define i32 @lp_uni_cpwidth(i32 %cp) {\n")
|
||||
emith(" %a = icmp ult i32 %cp, 128\n %b = icmp ult i32 %cp, 2048\n %c = icmp ult i32 %cp, 65536\n")
|
||||
emith(" %w34 = select i1 %c, i32 3, i32 4\n %w234 = select i1 %b, i32 2, i32 %w34\n %w = select i1 %a, i32 1, i32 %w234\n ret i32 %w\n}\n")
|
||||
|
||||
# encode a code point into %dst at byte offset %off; return the new offset
|
||||
emith("define i32 @fn_uni_encode(ptr %dst, i32 %off, i32 %cp) {\n")
|
||||
emith("entry:\n %w = call i32 @fn_uni_cpwidth(i32 %cp)\n %is1 = icmp eq i32 %w, 1\n br i1 %is1, label %e1, label %k2\n")
|
||||
emith("define i32 @lp_uni_encode(ptr %dst, i32 %off, i32 %cp) {\n")
|
||||
emith("entry:\n %w = call i32 @lp_uni_cpwidth(i32 %cp)\n %is1 = icmp eq i32 %w, 1\n br i1 %is1, label %e1, label %k2\n")
|
||||
emith("e1:\n %d0 = getelementptr i8, ptr %dst, i32 %off\n %b0 = trunc i32 %cp to i8\n store i8 %b0, ptr %d0\n %o1 = add i32 %off, 1\n ret i32 %o1\n")
|
||||
emith("k2:\n %is2 = icmp eq i32 %w, 2\n br i1 %is2, label %e2, label %k3\n")
|
||||
emith("e2:\n %hi2 = lshr i32 %cp, 6\n %by0 = or i32 %hi2, 192\n %lo2 = and i32 %cp, 63\n %by1 = or i32 %lo2, 128\n")
|
||||
|
|
@ -220,35 +220,35 @@ function emit_uni_case_fns() -> void {
|
|||
# is this code point a grapheme "extend" (a combining mark or variation
|
||||
# selector that joins the preceding base)? An approximation of the common
|
||||
# ranges; ZWJ and regional indicators are handled by grapheme_len itself.
|
||||
emith("define i32 @fn_uni_is_extend(i32 %c) {\n")
|
||||
emith(" %r1 = call i32 @fn_uni_inrange(i32 %c, i32 768, i32 879)\n") # 0300-036F combining diacritics
|
||||
emith(" %r2 = call i32 @fn_uni_inrange(i32 %c, i32 6832, i32 6911)\n") # 1AB0-1AFF
|
||||
emith(" %r3 = call i32 @fn_uni_inrange(i32 %c, i32 7616, i32 7679)\n") # 1DC0-1DFF
|
||||
emith(" %r4 = call i32 @fn_uni_inrange(i32 %c, i32 8400, i32 8447)\n") # 20D0-20FF
|
||||
emith(" %r5 = call i32 @fn_uni_inrange(i32 %c, i32 65056, i32 65071)\n") # FE20-FE2F
|
||||
emith(" %r6 = call i32 @fn_uni_inrange(i32 %c, i32 65024, i32 65039)\n") # FE00-FE0F variation selectors
|
||||
emith(" %r7 = call i32 @fn_uni_inrange(i32 %c, i32 917760, i32 917999)\n") # E0100-E01EF
|
||||
emith("define i32 @lp_uni_is_extend(i32 %c) {\n")
|
||||
emith(" %r1 = call i32 @lp_uni_inrange(i32 %c, i32 768, i32 879)\n") # 0300-036F combining diacritics
|
||||
emith(" %r2 = call i32 @lp_uni_inrange(i32 %c, i32 6832, i32 6911)\n") # 1AB0-1AFF
|
||||
emith(" %r3 = call i32 @lp_uni_inrange(i32 %c, i32 7616, i32 7679)\n") # 1DC0-1DFF
|
||||
emith(" %r4 = call i32 @lp_uni_inrange(i32 %c, i32 8400, i32 8447)\n") # 20D0-20FF
|
||||
emith(" %r5 = call i32 @lp_uni_inrange(i32 %c, i32 65056, i32 65071)\n") # FE20-FE2F
|
||||
emith(" %r6 = call i32 @lp_uni_inrange(i32 %c, i32 65024, i32 65039)\n") # FE00-FE0F variation selectors
|
||||
emith(" %r7 = call i32 @lp_uni_inrange(i32 %c, i32 917760, i32 917999)\n") # E0100-E01EF
|
||||
emith(" %o1 = or i32 %r1, %r2\n %o2 = or i32 %o1, %r3\n %o3 = or i32 %o2, %r4\n %o4 = or i32 %o3, %r5\n %o5 = or i32 %o4, %r6\n %o6 = or i32 %o5, %r7\n ret i32 %o6\n}\n")
|
||||
|
||||
emith("define i32 @fn_uni_inrange(i32 %c, i32 %lo, i32 %hi) {\n")
|
||||
emith("define i32 @lp_uni_inrange(i32 %c, i32 %lo, i32 %hi) {\n")
|
||||
emith(" %a = icmp uge i32 %c, %lo\n %b = icmp ule i32 %c, %hi\n %x = and i1 %a, %b\n %r = zext i1 %x to i32\n ret i32 %r\n}\n")
|
||||
}
|
||||
|
||||
# ---- code-point access + string builders -----------------------------------
|
||||
function emit_uni_str_fns() -> void {
|
||||
# the idx-th code point, or -1 when idx is past the end
|
||||
emith("define i32 @fn_uni_char_at(ptr %s, i32 %idx) {\n")
|
||||
emith("define i32 @lp_uni_char_at(ptr %s, i32 %idx) {\n")
|
||||
emith("entry:\n %ip = alloca i32\n %kp = alloca i32\n %cp = alloca i32\n store i32 0, ptr %ip\n store i32 0, ptr %kp\n br label %lp\n")
|
||||
emith("lp:\n %i = load i32, ptr %ip\n %p = getelementptr i8, ptr %s, i32 %i\n %b = load i8, ptr %p\n %c = zext i8 %b to i32\n")
|
||||
emith(" %z = icmp eq i32 %c, 0\n br i1 %z, label %none, label %go\n")
|
||||
emith("go:\n %ni = call i32 @fn_uni_decode(ptr %s, i32 %i, ptr %cp)\n %k = load i32, ptr %kp\n %hit = icmp eq i32 %k, %idx\n br i1 %hit, label %found, label %next\n")
|
||||
emith("go:\n %ni = call i32 @lp_uni_decode(ptr %s, i32 %i, ptr %cp)\n %k = load i32, ptr %kp\n %hit = icmp eq i32 %k, %idx\n br i1 %hit, label %found, label %next\n")
|
||||
emith("found:\n %v = load i32, ptr %cp\n ret i32 %v\n")
|
||||
emith("next:\n %k1 = add i32 %k, 1\n store i32 %k1, ptr %kp\n store i32 %ni, ptr %ip\n br label %lp\n")
|
||||
emith("none:\n ret i32 -1\n}\n")
|
||||
|
||||
# chars(s) -> []int : a fresh %LSlice of every code point, in order
|
||||
emith("define ptr @fn_uni_chars(ptr %s) {\n")
|
||||
emith("entry:\n %n = call i32 @fn_uni_len(ptr %s)\n %h = call ptr @malloc(i64 16)\n")
|
||||
emith("define ptr @lp_uni_chars(ptr %s) {\n")
|
||||
emith("entry:\n %n = call i32 @lp_uni_len(ptr %s)\n %h = call ptr @malloc(i64 16)\n")
|
||||
emith(" %nz = zext i32 %n to i64\n %bytes = mul i64 %nz, 4\n %data = call ptr @malloc(i64 %bytes)\n")
|
||||
emith(" %d0 = getelementptr inbounds %LSlice, ptr %h, i32 0, i32 0\n store ptr %data, ptr %d0\n")
|
||||
emith(" %d1 = getelementptr inbounds %LSlice, ptr %h, i32 0, i32 1\n store i32 %n, ptr %d1\n")
|
||||
|
|
@ -256,31 +256,31 @@ function emit_uni_str_fns() -> void {
|
|||
emith(" %ip = alloca i32\n %kp = alloca i32\n %cp = alloca i32\n store i32 0, ptr %ip\n store i32 0, ptr %kp\n br label %lp\n")
|
||||
emith("lp:\n %i = load i32, ptr %ip\n %p = getelementptr i8, ptr %s, i32 %i\n %b = load i8, ptr %p\n %c = zext i8 %b to i32\n")
|
||||
emith(" %z = icmp eq i32 %c, 0\n br i1 %z, label %done, label %go\n")
|
||||
emith("go:\n %ni = call i32 @fn_uni_decode(ptr %s, i32 %i, ptr %cp)\n %v = load i32, ptr %cp\n %k = load i32, ptr %kp\n")
|
||||
emith("go:\n %ni = call i32 @lp_uni_decode(ptr %s, i32 %i, ptr %cp)\n %v = load i32, ptr %cp\n %k = load i32, ptr %kp\n")
|
||||
emith(" %slot = getelementptr i32, ptr %data, i32 %k\n store i32 %v, ptr %slot\n %k1 = add i32 %k, 1\n store i32 %k1, ptr %kp\n store i32 %ni, ptr %ip\n br label %lp\n")
|
||||
emith("done:\n ret ptr %h\n}\n")
|
||||
|
||||
# case map the whole string. %up != 0 -> uppercase, else lowercase. Decodes,
|
||||
# maps each code point, and re-encodes into a fresh buffer (worst case 4 bytes
|
||||
# per code point, though ASCII/Latin-1 mapping preserves byte length).
|
||||
emith("define ptr @fn_uni_case(ptr %s, i32 %up) {\n")
|
||||
emith("define ptr @lp_uni_case(ptr %s, i32 %up) {\n")
|
||||
emith("entry:\n %bl = call i64 @strlen(ptr %s)\n %cap0 = mul i64 %bl, 4\n %cap = add i64 %cap0, 4\n %out = call ptr @malloc(i64 %cap)\n")
|
||||
emith(" %ip = alloca i32\n %op = alloca i32\n %cp = alloca i32\n store i32 0, ptr %ip\n store i32 0, ptr %op\n br label %lp\n")
|
||||
emith("lp:\n %i = load i32, ptr %ip\n %p = getelementptr i8, ptr %s, i32 %i\n %b = load i8, ptr %p\n %c = zext i8 %b to i32\n")
|
||||
emith(" %z = icmp eq i32 %c, 0\n br i1 %z, label %done, label %go\n")
|
||||
emith("go:\n %ni = call i32 @fn_uni_decode(ptr %s, i32 %i, ptr %cp)\n %v = load i32, ptr %cp\n")
|
||||
emith(" %mu = call i32 @fn_uni_upcp(i32 %v)\n %ml = call i32 @fn_uni_locp(i32 %v)\n %isup = icmp ne i32 %up, 0\n %m = select i1 %isup, i32 %mu, i32 %ml\n")
|
||||
emith(" %o = load i32, ptr %op\n %no = call i32 @fn_uni_encode(ptr %out, i32 %o, i32 %m)\n store i32 %no, ptr %op\n store i32 %ni, ptr %ip\n br label %lp\n")
|
||||
emith("go:\n %ni = call i32 @lp_uni_decode(ptr %s, i32 %i, ptr %cp)\n %v = load i32, ptr %cp\n")
|
||||
emith(" %mu = call i32 @lp_uni_upcp(i32 %v)\n %ml = call i32 @lp_uni_locp(i32 %v)\n %isup = icmp ne i32 %up, 0\n %m = select i1 %isup, i32 %mu, i32 %ml\n")
|
||||
emith(" %o = load i32, ptr %op\n %no = call i32 @lp_uni_encode(ptr %out, i32 %o, i32 %m)\n store i32 %no, ptr %op\n store i32 %ni, ptr %ip\n br label %lp\n")
|
||||
emith("done:\n %fo = load i32, ptr %op\n %endp = getelementptr i8, ptr %out, i32 %fo\n store i8 0, ptr %endp\n ret ptr %out\n}\n")
|
||||
|
||||
# truncate(s, n) -> the first n code points as a fresh string (never splits a
|
||||
# multibyte character). n <= 0 yields the empty string.
|
||||
emith("define ptr @fn_uni_truncate(ptr %s, i32 %n) {\n")
|
||||
emith("define ptr @lp_uni_truncate(ptr %s, i32 %n) {\n")
|
||||
emith("entry:\n %ip = alloca i32\n %kp = alloca i32\n %cp = alloca i32\n store i32 0, ptr %ip\n store i32 0, ptr %kp\n br label %lp\n")
|
||||
emith("lp:\n %k = load i32, ptr %kp\n %enough = icmp sge i32 %k, %n\n br i1 %enough, label %cut, label %chk\n")
|
||||
emith("chk:\n %i = load i32, ptr %ip\n %p = getelementptr i8, ptr %s, i32 %i\n %b = load i8, ptr %p\n %c = zext i8 %b to i32\n")
|
||||
emith(" %z = icmp eq i32 %c, 0\n br i1 %z, label %cut, label %go\n")
|
||||
emith("go:\n %ni = call i32 @fn_uni_decode(ptr %s, i32 %i, ptr %cp)\n %k1 = add i32 %k, 1\n store i32 %k1, ptr %kp\n store i32 %ni, ptr %ip\n br label %lp\n")
|
||||
emith("go:\n %ni = call i32 @lp_uni_decode(ptr %s, i32 %i, ptr %cp)\n %k1 = add i32 %k, 1\n store i32 %k1, ptr %kp\n store i32 %ni, ptr %ip\n br label %lp\n")
|
||||
emith("cut:\n %len = load i32, ptr %ip\n %lz = zext i32 %len to i64\n %cap = add i64 %lz, 1\n %out = call ptr @malloc(i64 %cap)\n %lz2 = zext i32 %len to i64\n call ptr @memcpy(ptr %out, ptr %s, i64 %lz2)\n")
|
||||
emith(" %endp = getelementptr i8, ptr %out, i32 %len\n store i8 0, ptr %endp\n ret ptr %out\n}\n")
|
||||
|
||||
|
|
@ -292,26 +292,26 @@ function emit_uni_str_fns() -> void {
|
|||
# ZWJ emoji sequences count as one); and the second regional indicator of a flag
|
||||
# pair. Simple state carried in allocas.
|
||||
function emit_uni_grapheme() -> void {
|
||||
emith("define i32 @fn_uni_grapheme_len(ptr %s) {\n")
|
||||
emith("define i32 @lp_uni_grapheme_len(ptr %s) {\n")
|
||||
emith("entry:\n %ip = alloca i32\n %np = alloca i32\n %zp = alloca i32\n %rp = alloca i32\n %cp = alloca i32\n")
|
||||
emith(" store i32 0, ptr %ip\n store i32 0, ptr %np\n store i32 0, ptr %zp\n store i32 0, ptr %rp\n br label %lp\n")
|
||||
emith("lp:\n %i = load i32, ptr %ip\n %p = getelementptr i8, ptr %s, i32 %i\n %b = load i8, ptr %p\n %c = zext i8 %b to i32\n")
|
||||
emith(" %z = icmp eq i32 %c, 0\n br i1 %z, label %done, label %go\n")
|
||||
emith("go:\n %ni = call i32 @fn_uni_decode(ptr %s, i32 %i, ptr %cp)\n %v = load i32, ptr %cp\n store i32 %ni, ptr %ip\n")
|
||||
emith("go:\n %ni = call i32 @lp_uni_decode(ptr %s, i32 %i, ptr %cp)\n %v = load i32, ptr %cp\n store i32 %ni, ptr %ip\n")
|
||||
# ZWJ (U+200D): extends the cluster and arms the join for the next code point
|
||||
emith(" %iszwj = icmp eq i32 %v, 8205\n br i1 %iszwj, label %zwj, label %notzwj\n")
|
||||
emith("zwj:\n %n0 = load i32, ptr %np\n %n0z = icmp eq i32 %n0, 0\n %n0b = zext i1 %n0z to i32\n %n0n = add i32 %n0, %n0b\n store i32 %n0n, ptr %np\n") # leading ZWJ still opens one cluster
|
||||
emith(" store i32 1, ptr %zp\n store i32 0, ptr %rp\n br label %lp\n")
|
||||
emith("notzwj:\n %n = load i32, ptr %np\n %first = icmp eq i32 %n, 0\n br i1 %first, label %open, label %cont\n")
|
||||
# first cluster
|
||||
emith("open:\n store i32 1, ptr %np\n store i32 0, ptr %zp\n %ri0 = call i32 @fn_uni_inrange(i32 %v, i32 127462, i32 127487)\n store i32 %ri0, ptr %rp\n br label %lp\n")
|
||||
emith("open:\n store i32 1, ptr %np\n store i32 0, ptr %zp\n %ri0 = call i32 @lp_uni_inrange(i32 %v, i32 127462, i32 127487)\n store i32 %ri0, ptr %rp\n br label %lp\n")
|
||||
emith("cont:\n %zj = load i32, ptr %zp\n %afterz = icmp ne i32 %zj, 0\n br i1 %afterz, label %joinz, label %chkext\n")
|
||||
# code point right after a ZWJ joins the current cluster
|
||||
emith("joinz:\n store i32 0, ptr %zp\n store i32 0, ptr %rp\n br label %lp\n")
|
||||
emith("chkext:\n %ext = call i32 @fn_uni_is_extend(i32 %v)\n %isext = icmp ne i32 %ext, 0\n br i1 %isext, label %joinext, label %chkri\n")
|
||||
emith("chkext:\n %ext = call i32 @lp_uni_is_extend(i32 %v)\n %isext = icmp ne i32 %ext, 0\n br i1 %isext, label %joinext, label %chkri\n")
|
||||
emith("joinext:\n store i32 0, ptr %rp\n br label %lp\n")
|
||||
# regional indicator: joins only as the second of a pair
|
||||
emith("chkri:\n %ri = call i32 @fn_uni_inrange(i32 %v, i32 127462, i32 127487)\n %isri = icmp ne i32 %ri, 0\n %ropen = load i32, ptr %rp\n %ropenb = icmp ne i32 %ropen, 0\n %pair = and i1 %isri, %ropenb\n br i1 %pair, label %joinri, label %newcl\n")
|
||||
emith("chkri:\n %ri = call i32 @lp_uni_inrange(i32 %v, i32 127462, i32 127487)\n %isri = icmp ne i32 %ri, 0\n %ropen = load i32, ptr %rp\n %ropenb = icmp ne i32 %ropen, 0\n %pair = and i1 %isri, %ropenb\n br i1 %pair, label %joinri, label %newcl\n")
|
||||
emith("joinri:\n store i32 0, ptr %rp\n br label %lp\n")
|
||||
emith("newcl:\n %nn = load i32, ptr %np\n %nn1 = add i32 %nn, 1\n store i32 %nn1, ptr %np\n store i32 0, ptr %zp\n %riset = select i1 %isri, i32 1, i32 0\n store i32 %riset, ptr %rp\n br label %lp\n")
|
||||
emith("done:\n %r = load i32, ptr %np\n ret i32 %r\n}\n")
|
||||
|
|
|
|||
|
|
@ -37,17 +37,17 @@ function emit_uuid_ns(meth: pointer, e: Node) -> Val {
|
|||
g_uses_cryptort = true
|
||||
g_uses_uuidrt = true
|
||||
if (meth == "new") or (meth == "v4") { # v4: 122 random bits
|
||||
return val(emit_bind("call ptr @fn_uuid_v4()"), "string")
|
||||
return val(emit_bind("call ptr @lp_uuid_v4()"), "string")
|
||||
}
|
||||
if (meth == "new_v7") or (meth == "v7") { # v7: ms timestamp + random
|
||||
return val(emit_bind("call ptr @fn_uuid_v7()"), "string")
|
||||
return val(emit_bind("call ptr @lp_uuid_v7()"), "string")
|
||||
}
|
||||
if (meth == "nil") { # the all-zero UUID
|
||||
return val(emit_bind("call ptr @fn_uuid_nil()"), "string")
|
||||
return val(emit_bind("call ptr @lp_uuid_nil()"), "string")
|
||||
}
|
||||
if (meth == "is_valid") { # well-formed UUID? -> bool
|
||||
let s = emit_expr(e.kids[0])
|
||||
return val(emit_bind(`call i32 @fn_uuid_valid(ptr {s.code})`), "bool")
|
||||
return val(emit_bind(`call i32 @lp_uuid_valid(ptr {s.code})`), "bool")
|
||||
}
|
||||
if (meth == "to_text") { # already canonical text: identity
|
||||
let s = emit_expr(e.kids[0])
|
||||
|
|
@ -55,13 +55,13 @@ function emit_uuid_ns(meth: pointer, e: Node) -> Val {
|
|||
}
|
||||
if (meth == "equals") { # case-insensitive equality -> bool
|
||||
let a = emit_expr(e.kids[0]); let b = emit_expr(e.kids[1])
|
||||
return val(emit_bind(`call i32 @fn_uuid_eq(ptr {a.code}, ptr {b.code})`), "bool")
|
||||
return val(emit_bind(`call i32 @lp_uuid_eq(ptr {a.code}, ptr {b.code})`), "bool")
|
||||
}
|
||||
# parse(s): normalise an untrusted string to a lowercase UUID, or the nil UUID
|
||||
# when it is not well-formed. Callers that must reject bad input should gate on
|
||||
# Uuid.is_valid(s) first; this never faults on garbage.
|
||||
let s = emit_expr(e.kids[0])
|
||||
return val(emit_bind(`call ptr @fn_uuid_parse(ptr {s.code})`), "string")
|
||||
return val(emit_bind(`call ptr @lp_uuid_parse(ptr {s.code})`), "string")
|
||||
}
|
||||
|
||||
# emit_uuid_prelude — the UUID runtime, emitted once per program that uses Uuid.*
|
||||
|
|
@ -71,8 +71,8 @@ function emit_uuid_ns(meth: pointer, e: Node) -> Val {
|
|||
function emit_uuid_prelude() -> void {
|
||||
# 16 raw bytes -> a fresh canonical 36-char string. hex-encode all 16 bytes,
|
||||
# then splice the four hyphens between the 8/4/4/4/12 groups.
|
||||
emith("define ptr @fn_uuid_format(ptr %b16) {\n")
|
||||
emith("entry:\n %hex = call ptr @fn_hex_encode(ptr %b16, i64 16)\n %out = call ptr @malloc(i64 37)\n")
|
||||
emith("define ptr @lp_uuid_format(ptr %b16) {\n")
|
||||
emith("entry:\n %hex = call ptr @lp_hex_encode(ptr %b16, i64 16)\n %out = call ptr @malloc(i64 37)\n")
|
||||
emith(" call ptr @memcpy(ptr %out, ptr %hex, i64 8)\n")
|
||||
emith(" %o8 = getelementptr i8, ptr %out, i64 8\n store i8 45, ptr %o8\n")
|
||||
emith(" %h8 = getelementptr i8, ptr %hex, i64 8\n %o9 = getelementptr i8, ptr %out, i64 9\n call ptr @memcpy(ptr %o9, ptr %h8, i64 4)\n")
|
||||
|
|
@ -87,18 +87,18 @@ function emit_uuid_prelude() -> void {
|
|||
|
||||
# v4: 16 CSPRNG bytes, then set version (0x4x in byte 6) and variant (0b10xx in
|
||||
# byte 8). 0x80 does not fit an i8 immediate, so it is written as -128.
|
||||
emith("define ptr @fn_uuid_v4() {\n")
|
||||
emith("entry:\n %b = alloca [16 x i8]\n %bp = getelementptr [16 x i8], ptr %b, i64 0, i64 0\n call void @fn_secure_bytes(ptr %bp, i64 16)\n")
|
||||
emith("define ptr @lp_uuid_v4() {\n")
|
||||
emith("entry:\n %b = alloca [16 x i8]\n %bp = getelementptr [16 x i8], ptr %b, i64 0, i64 0\n call void @lp_secure_bytes(ptr %bp, i64 16)\n")
|
||||
emith(" %p6 = getelementptr i8, ptr %bp, i64 6\n %v6 = load i8, ptr %p6\n %v6a = and i8 %v6, 15\n %v6b = or i8 %v6a, 64\n store i8 %v6b, ptr %p6\n")
|
||||
emith(" %p8 = getelementptr i8, ptr %bp, i64 8\n %v8 = load i8, ptr %p8\n %v8a = and i8 %v8, 63\n %v8b = or i8 %v8a, -128\n store i8 %v8b, ptr %p8\n")
|
||||
emith(" %s = call ptr @fn_uuid_format(ptr %bp)\n ret ptr %s\n}\n")
|
||||
emith(" %s = call ptr @lp_uuid_format(ptr %bp)\n ret ptr %s\n}\n")
|
||||
|
||||
# v7: random fill, then overwrite the first 6 bytes with a 48-bit big-endian
|
||||
# Unix-millisecond timestamp; set version 7 (0x7x) and the variant. Sub-second
|
||||
# resolution is derived from time() seconds * 1000 — monotonic per second, with
|
||||
# the random tail keeping same-millisecond IDs distinct.
|
||||
emith("define ptr @fn_uuid_v7() {\n")
|
||||
emith("entry:\n %b = alloca [16 x i8]\n %bp = getelementptr [16 x i8], ptr %b, i64 0, i64 0\n call void @fn_secure_bytes(ptr %bp, i64 16)\n")
|
||||
emith("define ptr @lp_uuid_v7() {\n")
|
||||
emith("entry:\n %b = alloca [16 x i8]\n %bp = getelementptr [16 x i8], ptr %b, i64 0, i64 0\n call void @lp_secure_bytes(ptr %bp, i64 16)\n")
|
||||
emith(" %t = call i64 @time(ptr null)\n %ms = mul i64 %t, 1000\n")
|
||||
emith(" %s40 = lshr i64 %ms, 40\n %t0 = trunc i64 %s40 to i8\n %q0 = getelementptr i8, ptr %bp, i64 0\n store i8 %t0, ptr %q0\n")
|
||||
emith(" %s32 = lshr i64 %ms, 32\n %t1 = trunc i64 %s32 to i8\n %q1 = getelementptr i8, ptr %bp, i64 1\n store i8 %t1, ptr %q1\n")
|
||||
|
|
@ -108,10 +108,10 @@ function emit_uuid_prelude() -> void {
|
|||
emith(" %t5 = trunc i64 %ms to i8\n %q5 = getelementptr i8, ptr %bp, i64 5\n store i8 %t5, ptr %q5\n")
|
||||
emith(" %p6 = getelementptr i8, ptr %bp, i64 6\n %v6 = load i8, ptr %p6\n %v6a = and i8 %v6, 15\n %v6b = or i8 %v6a, 112\n store i8 %v6b, ptr %p6\n")
|
||||
emith(" %p8 = getelementptr i8, ptr %bp, i64 8\n %v8 = load i8, ptr %p8\n %v8a = and i8 %v8, 63\n %v8b = or i8 %v8a, -128\n store i8 %v8b, ptr %p8\n")
|
||||
emith(" %s = call ptr @fn_uuid_format(ptr %bp)\n ret ptr %s\n}\n")
|
||||
emith(" %s = call ptr @lp_uuid_format(ptr %bp)\n ret ptr %s\n}\n")
|
||||
|
||||
# the nil UUID: 36 '0' with hyphens spliced in
|
||||
emith("define ptr @fn_uuid_nil() {\n")
|
||||
emith("define ptr @lp_uuid_nil() {\n")
|
||||
emith("entry:\n %out = call ptr @malloc(i64 37)\n call ptr @memset(ptr %out, i32 48, i64 36)\n")
|
||||
emith(" %o8 = getelementptr i8, ptr %out, i64 8\n store i8 45, ptr %o8\n")
|
||||
emith(" %o13 = getelementptr i8, ptr %out, i64 13\n store i8 45, ptr %o13\n")
|
||||
|
|
@ -120,7 +120,7 @@ function emit_uuid_prelude() -> void {
|
|||
emith(" %o36 = getelementptr i8, ptr %out, i64 36\n store i8 0, ptr %o36\n ret ptr %out\n}\n")
|
||||
|
||||
# is %s a well-formed UUID? length 36, hyphens at 8/13/18/23, hex elsewhere.
|
||||
emith("define i32 @fn_uuid_valid(ptr %s) {\n")
|
||||
emith("define i32 @lp_uuid_valid(ptr %s) {\n")
|
||||
emith("entry:\n %n = call i64 @strlen(ptr %s)\n %ne = icmp eq i64 %n, 36\n br i1 %ne, label %go, label %bad\n")
|
||||
emith("go:\n %ip = alloca i64\n store i64 0, ptr %ip\n br label %cond\n")
|
||||
emith("cond:\n %i = load i64, ptr %ip\n %lt = icmp ult i64 %i, 36\n br i1 %lt, label %body, label %good\n")
|
||||
|
|
@ -138,7 +138,7 @@ function emit_uuid_prelude() -> void {
|
|||
emith("bad:\n ret i32 0\n}\n")
|
||||
|
||||
# case-insensitive equality of two null-terminated strings -> i32 bool
|
||||
emith("define i32 @fn_uuid_eq(ptr %a, ptr %b) {\n")
|
||||
emith("define i32 @lp_uuid_eq(ptr %a, ptr %b) {\n")
|
||||
emith("entry:\n %la = call i64 @strlen(ptr %a)\n %lb = call i64 @strlen(ptr %b)\n %eq = icmp eq i64 %la, %lb\n br i1 %eq, label %go, label %ne\n")
|
||||
emith("go:\n %ip = alloca i64\n store i64 0, ptr %ip\n br label %cond\n")
|
||||
emith("cond:\n %i = load i64, ptr %ip\n %lt = icmp ult i64 %i, %la\n br i1 %lt, label %body, label %eqret\n")
|
||||
|
|
@ -151,8 +151,8 @@ function emit_uuid_prelude() -> void {
|
|||
emith("ne:\n ret i32 0\n}\n")
|
||||
|
||||
# parse: lowercase-normalise a valid UUID, else return the nil UUID.
|
||||
emith("define ptr @fn_uuid_parse(ptr %s) {\n")
|
||||
emith("entry:\n %ok = call i32 @fn_uuid_valid(ptr %s)\n %isok = icmp ne i32 %ok, 0\n br i1 %isok, label %dup, label %nilb\n")
|
||||
emith("define ptr @lp_uuid_parse(ptr %s) {\n")
|
||||
emith("entry:\n %ok = call i32 @lp_uuid_valid(ptr %s)\n %isok = icmp ne i32 %ok, 0\n br i1 %isok, label %dup, label %nilb\n")
|
||||
emith("dup:\n %out = call ptr @malloc(i64 37)\n %ip = alloca i64\n store i64 0, ptr %ip\n br label %cond\n")
|
||||
emith("cond:\n %i = load i64, ptr %ip\n %lt = icmp ult i64 %i, 36\n br i1 %lt, label %body, label %fin\n")
|
||||
emith("body:\n %p = getelementptr i8, ptr %s, i64 %i\n %c = load i8, ptr %p\n")
|
||||
|
|
@ -160,5 +160,5 @@ function emit_uuid_prelude() -> void {
|
|||
emith(" %op = getelementptr i8, ptr %out, i64 %i\n store i8 %cl, ptr %op\n")
|
||||
emith(" %i1 = add i64 %i, 1\n store i64 %i1, ptr %ip\n br label %cond\n")
|
||||
emith("fin:\n %o36 = getelementptr i8, ptr %out, i64 36\n store i8 0, ptr %o36\n ret ptr %out\n")
|
||||
emith("nilb:\n %nn = call ptr @fn_uuid_nil()\n ret ptr %nn\n}\n")
|
||||
emith("nilb:\n %nn = call ptr @lp_uuid_nil()\n ret ptr %nn\n}\n")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ function emit_vector_ns(meth: pointer, e: Node) -> Val {
|
|||
let vx = vec_x(v.code); let vy = vec_y(v.code)
|
||||
let xx = fx_mul_code(vx, vx); let yy = fx_mul_code(vy, vy)
|
||||
let s = emit_bind(`add i32 {xx}, {yy}`)
|
||||
return val(emit_bind(`call i32 @fn_fx_sqrt(i32 {s})`), "fixed")
|
||||
return val(emit_bind(`call i32 @lp_fx_sqrt(i32 {s})`), "fixed")
|
||||
}
|
||||
if (meth == "distance") { # length(a - b) -> fixed
|
||||
g_uses_mathrt = true
|
||||
|
|
@ -95,7 +95,7 @@ function emit_vector_ns(meth: pointer, e: Node) -> Val {
|
|||
let dy = emit_bind(`sub i32 {ay}, {by}`)
|
||||
let xx = fx_mul_code(dx, dx); let yy = fx_mul_code(dy, dy)
|
||||
let s = emit_bind(`add i32 {xx}, {yy}`)
|
||||
return val(emit_bind(`call i32 @fn_fx_sqrt(i32 {s})`), "fixed")
|
||||
return val(emit_bind(`call i32 @lp_fx_sqrt(i32 {s})`), "fixed")
|
||||
}
|
||||
if (meth == "normalize") { # v / length(v); the zero vector maps to itself
|
||||
g_uses_mathrt = true
|
||||
|
|
@ -103,7 +103,7 @@ function emit_vector_ns(meth: pointer, e: Node) -> Val {
|
|||
let vx = vec_x(v.code); let vy = vec_y(v.code)
|
||||
let xx = fx_mul_code(vx, vx); let yy = fx_mul_code(vy, vy)
|
||||
let s = emit_bind(`add i32 {xx}, {yy}`)
|
||||
let len = emit_bind(`call i32 @fn_fx_sqrt(i32 {s})`)
|
||||
let len = emit_bind(`call i32 @lp_fx_sqrt(i32 {s})`)
|
||||
let zero = emit_bind(`icmp eq i32 {len}, 0`)
|
||||
let denom = emit_bind(`select i1 {zero}, i32 65536, i32 {len}`) # avoid divide-by-zero
|
||||
let inv = fx_div_code("65536", denom)
|
||||
|
|
@ -113,9 +113,9 @@ function emit_vector_ns(meth: pointer, e: Node) -> Val {
|
|||
if (meth == "rotate") { # rotate by angle (radians, fixed)
|
||||
g_uses_mathrt = true
|
||||
let v = emit_expr(e.kids[0]); let ang = emit_expr(e.kids[1])
|
||||
let sn = emit_bind(`call i32 @fn_fx_sin(i32 {ang.code})`)
|
||||
let sn = emit_bind(`call i32 @lp_fx_sin(i32 {ang.code})`)
|
||||
let ca = emit_bind(`add i32 {ang.code}, 102944`) # cos(a) = sin(a + pi/2)
|
||||
let cs = emit_bind(`call i32 @fn_fx_sin(i32 {ca})`)
|
||||
let cs = emit_bind(`call i32 @lp_fx_sin(i32 {ca})`)
|
||||
let vx = vec_x(v.code); let vy = vec_y(v.code)
|
||||
let xc = fx_mul_code(vx, cs); let ys = fx_mul_code(vy, sn)
|
||||
let xs = fx_mul_code(vx, sn); let yc = fx_mul_code(vy, cs)
|
||||
|
|
@ -127,14 +127,14 @@ function emit_vector_ns(meth: pointer, e: Node) -> Val {
|
|||
g_uses_mathrt = true
|
||||
let v = emit_expr(e.kids[0])
|
||||
let vx = vec_x(v.code); let vy = vec_y(v.code)
|
||||
return val(emit_bind(`call i32 @fn_fx_atan2(i32 {vy}, i32 {vx})`), "fixed")
|
||||
return val(emit_bind(`call i32 @lp_fx_atan2(i32 {vy}, i32 {vx})`), "fixed")
|
||||
}
|
||||
if (meth == "from_angle") { # unit vector at angle a: (cos a, sin a)
|
||||
g_uses_mathrt = true
|
||||
let ang = emit_expr(e.kids[0])
|
||||
let sn = emit_bind(`call i32 @fn_fx_sin(i32 {ang.code})`)
|
||||
let sn = emit_bind(`call i32 @lp_fx_sin(i32 {ang.code})`)
|
||||
let ca = emit_bind(`add i32 {ang.code}, 102944`)
|
||||
let cs = emit_bind(`call i32 @fn_fx_sin(i32 {ca})`)
|
||||
let cs = emit_bind(`call i32 @lp_fx_sin(i32 {ca})`)
|
||||
return val(vec_pack(cs, sn), "Vector")
|
||||
}
|
||||
# lerp(a, b, t: fixed) -> Vector — component-wise linear interpolation
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue