ludic/selfhost/backend/stdlib/emit_math.ludic
Orkuncakilkaya 23726afa90
All checks were successful
bootstrap / cfree-fixpoint (push) Successful in 12s
ci / build-and-test (push) Successful in 50s
commit-lint / conventional-commits (push) Successful in 3s
docs / build-and-deploy (push) Successful in 2s
refactor(selfhost): reorganise into concern-based subdirectories
Split the flat 38-file selfhost/ into concern-based subdirectories:

  frontend/        lex, parse, parse_game, ast
  support/         str, buf, io
  backend/         core IR + expression/statement lowering
  backend/game/    ECS/scene/event/world lowering
  backend/stdlib/  the namespaced Math.*/Text.*/Crypto.*/… intrinsics

and split the three oversized emitters at responsibility boundaries so
no file mixes concerns:

  emit_game.ludic  -> + emit_world.ludic         (reflection world table,
                                                  tick helpers, @main synthesis)
  emit_expr.ludic  -> + emit_call.ludic          (namespaced builtins, call
                                                  lowering, expr dispatch)
  emit_text.ludic  -> + emit_text_prelude.ludic  (emitted string-builder runtime)

FRAGS in tools/x/selfhost.ludic is updated to the new paths with the link
order preserved, and the Python doc/vocabulary tooling is updated to walk
the new layout. Because the build is a plain in-order concatenation and
every split lands on a blank-line boundary, the regenerated seed is
byte-identical: `x reseed` leaves selfhost/ludicc.seed.ll unchanged,
`x bootstrap-cfree` still reaches its fixed point, and both `x test` (56)
and `x selfhost-test` (29, incl. golden renders) stay green.

Closes #29

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-31 00:26:02 +03:00

405 lines
24 KiB
Text

# 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
# plain integer IR, so it is bit-identical on every platform.
function is_math_builtin(name: pointer) -> bool {
return (name == "min") or (name == "max") or (name == "abs") or (name == "clamp")
}
function emit_math_builtin(name: pointer, e: Node) -> Val {
if (name == "abs") {
let a = emit_expr(e.kids[0])
let c = emit_bind(`icmp slt i32 {a.code}, 0`)
let n = emit_bind(`sub i32 0, {a.code}`)
return val(emit_bind(`select i1 {c}, i32 {n}, i32 {a.code}`), a.ty)
}
if (name == "min") or (name == "max") {
let a = emit_expr(e.kids[0]); let b = emit_expr(e.kids[1])
var op = "slt"
if (name == "max") { op = "sgt" }
let c = emit_bind(`icmp {op} i32 {a.code}, {b.code}`)
return val(emit_bind(`select i1 {c}, i32 {a.code}, i32 {b.code}`), a.ty)
}
# clamp(v, lo, hi) = max(lo, min(v, hi))
let v = emit_expr(e.kids[0]); let lo = emit_expr(e.kids[1]); let hi = emit_expr(e.kids[2])
let c1 = emit_bind(`icmp slt i32 {v.code}, {hi.code}`)
let t = emit_bind(`select i1 {c1}, i32 {v.code}, i32 {hi.code}`)
let c2 = emit_bind(`icmp sgt i32 {lo.code}, {t}`)
return val(emit_bind(`select i1 {c2}, i32 {lo.code}, i32 {t}`), v.ty)
}
# a * b in Q16.16 (64-bit intermediate, arithmetic shift back) -> code of an i32
function fx_mul_code(a: pointer, b: pointer) -> pointer {
let a64 = emit_bind(`sext i32 {a} to i64`)
let b64 = emit_bind(`sext i32 {b} to i64`)
let m = emit_bind(`mul i64 {a64}, {b64}`)
let sh = emit_bind(`ashr i64 {m}, 16`)
return emit_bind(`trunc i64 {sh} to i32`)
}
# a / b in Q16.16 (shift the numerator up before the divide) -> code of an i32
function fx_div_code(a: pointer, b: pointer) -> pointer {
let a64 = emit_bind(`sext i32 {a} to i64`)
let ash = emit_bind(`shl i64 {a64}, 16`)
let b64 = emit_bind(`sext i32 {b} to i64`)
let dv = emit_bind(`sdiv i64 {ash}, {b64}`)
return emit_bind(`trunc i64 {dv} to i32`)
}
# lerp(a, b, t) = a + (b - a) * t, all Q16.16 -> code of a fixed i32
function fx_lerp_code(a: pointer, b: pointer, t: pointer) -> pointer {
let d = emit_bind(`sub i32 {b}, {a}`)
let dt = fx_mul_code(d, t)
return emit_bind(`add i32 {a}, {dt}`)
}
# inverse_lerp(a, b, v) = (v - a) / (b - a), all Q16.16 -> code of a fixed i32
function fx_inv_lerp_code(a: pointer, b: pointer, v: pointer) -> pointer {
let num = emit_bind(`sub i32 {v}, {a}`)
let den = emit_bind(`sub i32 {b}, {a}`)
return fx_div_code(num, den)
}
# Math.* — the namespaced surface. min/max/abs/clamp reuse the bare lowering;
# the rest are new deterministic fixed-point helpers. Returns g_intrin-style via
# a direct Val; callers guard with is_math_ns first.
function is_math_ns(meth: pointer) -> bool {
if (meth == "min") or (meth == "max") or (meth == "abs") or (meth == "clamp") { return true }
if (meth == "sign") or (meth == "floor") or (meth == "ceil") or (meth == "round") { return true }
if (meth == "lerp") or (meth == "inverse_lerp") or (meth == "remap") { return true }
if (meth == "sqrt") or (meth == "sin") or (meth == "cos") or (meth == "tan") or (meth == "hypot") { return true }
if (meth == "atan2") or (meth == "asin") or (meth == "acos") { return true }
if (meth == "deg_to_rad") or (meth == "rad_to_deg") or (meth == "posmod") or (meth == "wrap") { return true }
if (meth == "ping_pong") or (meth == "snapped") or (meth == "move_toward") or (meth == "smoothstep") { return true }
if (meth == "dist") or (meth == "dist2") { return true }
if (meth == "exp") or (meth == "log") or (meth == "pow") { return true }
return false
}
function emit_math_ns(meth: pointer, e: Node) -> Val {
if (meth == "min") or (meth == "max") or (meth == "abs") or (meth == "clamp") {
return emit_math_builtin(meth, e)
}
if (meth == "sign") { # sign(x) -> -1 / 0 / 1 (int)
let a = emit_expr(e.kids[0])
let pos = emit_bind(`icmp sgt i32 {a.code}, 0`)
let neg = emit_bind(`icmp slt i32 {a.code}, 0`)
let lo = emit_bind(`select i1 {neg}, i32 -1, i32 0`)
return val(emit_bind(`select i1 {pos}, i32 1, i32 {lo}`), "int")
}
if (meth == "floor") { # floor(fixed) -> int
let a = emit_expr(e.kids[0])
return val(emit_bind(`ashr i32 {a.code}, 16`), "int")
}
if (meth == "ceil") { # ceil(fixed) -> int
let a = emit_expr(e.kids[0])
let t = emit_bind(`add i32 {a.code}, 65535`)
return val(emit_bind(`ashr i32 {t}, 16`), "int")
}
if (meth == "round") { # round(fixed) -> nearest int (half up)
let a = emit_expr(e.kids[0])
let t = emit_bind(`add i32 {a.code}, 32768`)
return val(emit_bind(`ashr i32 {t}, 16`), "int")
}
if (meth == "lerp") { # lerp(a, b, t: fixed) -> fixed
let a = emit_expr(e.kids[0]); let b = emit_expr(e.kids[1]); let t = emit_expr(e.kids[2])
return val(fx_lerp_code(a.code, b.code, t.code), "fixed")
}
if (meth == "inverse_lerp") { # inverse_lerp(a, b, v: fixed) -> fixed
let a = emit_expr(e.kids[0]); let b = emit_expr(e.kids[1]); let v = emit_expr(e.kids[2])
return val(fx_inv_lerp_code(a.code, b.code, v.code), "fixed")
}
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")
}
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")
}
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")
}
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 sh = emit_bind(`add i32 {a.code}, 102944`)
let c = emit_bind(`call i32 @fn_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")
}
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")
}
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")
}
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")
}
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])
let x1 = emit_expr(e.kids[2]); let y1 = emit_expr(e.kids[3])
let dx = emit_bind(`sub i32 {x1.code}, {x0.code}`)
let dy = emit_bind(`sub i32 {y1.code}, {y0.code}`)
let xx = fx_mul_code(dx, dx); let yy = fx_mul_code(dy, dy)
return val(emit_bind(`add i32 {xx}, {yy}`), "fixed")
}
if (meth == "dist") { # dist(x0,y0,x1,y1) = sqrt(dist2)
g_uses_mathrt = true
let x0 = emit_expr(e.kids[0]); let y0 = emit_expr(e.kids[1])
let x1 = emit_expr(e.kids[2]); let y1 = emit_expr(e.kids[3])
let dx = emit_bind(`sub i32 {x1.code}, {x0.code}`)
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")
}
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")
}
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})`)
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 t = fx_mul_code(b.code, l2)
return val(emit_bind(`call i32 @fn_fx_exp2(i32 {t})`), "fixed")
}
if (meth == "deg_to_rad") { # d * (pi/180), pi/180 = 1144 fixed
let d = emit_expr(e.kids[0])
return val(fx_mul_code(d.code, "1144"), "fixed")
}
if (meth == "rad_to_deg") { # r * (180/pi), 180/pi = 3754936 fixed
let r = emit_expr(e.kids[0])
return val(fx_mul_code(r.code, "3754936"), "fixed")
}
if (meth == "posmod") { # ((a % m) + m) % m, always in [0, m)
let a = emit_expr(e.kids[0]); let m = emit_expr(e.kids[1])
let r = emit_bind(`srem i32 {a.code}, {m.code}`)
let rm = emit_bind(`add i32 {r}, {m.code}`)
return val(emit_bind(`srem i32 {rm}, {m.code}`), "int")
}
if (meth == "wrap") { # wrap(v, lo, hi) into [lo, hi)
let v = emit_expr(e.kids[0]); let lo = emit_expr(e.kids[1]); let hi = emit_expr(e.kids[2])
let range = emit_bind(`sub i32 {hi.code}, {lo.code}`)
let off = emit_bind(`sub i32 {v.code}, {lo.code}`)
let r = emit_bind(`srem i32 {off}, {range}`)
let rm = emit_bind(`add i32 {r}, {range}`)
let pm = emit_bind(`srem i32 {rm}, {range}`)
return val(emit_bind(`add i32 {lo.code}, {pm}`), "int")
}
if (meth == "ping_pong") { # bounce 0..len..0, integer
let t = emit_expr(e.kids[0]); let l = emit_expr(e.kids[1])
let two = emit_bind(`mul i32 {l.code}, 2`)
let r = emit_bind(`srem i32 {t.code}, {two}`)
let rm = emit_bind(`add i32 {r}, {two}`)
let pm = emit_bind(`srem i32 {rm}, {two}`)
let sub = emit_bind(`sub i32 {pm}, {l.code}`)
let neg = emit_bind(`sub i32 0, {sub}`)
let c = emit_bind(`icmp slt i32 {sub}, 0`)
let ab = emit_bind(`select i1 {c}, i32 {neg}, i32 {sub}`)
return val(emit_bind(`sub i32 {l.code}, {ab}`), "int")
}
if (meth == "snapped") { # nearest multiple of step (fixed)
let v = emit_expr(e.kids[0]); let step = emit_expr(e.kids[1])
let q = fx_div_code(v.code, step.code)
let qr = emit_bind(`add i32 {q}, 32768`)
let n = emit_bind(`ashr i32 {qr}, 16`)
return val(emit_bind(`mul i32 {n}, {step.code}`), "fixed")
}
if (meth == "move_toward") { # step from -> to by at most delta (fixed)
let f = emit_expr(e.kids[0]); let to = emit_expr(e.kids[1]); let d = emit_expr(e.kids[2])
let diff = emit_bind(`sub i32 {to.code}, {f.code}`)
let dneg = emit_bind(`sub i32 0, {diff}`)
let dc = emit_bind(`icmp slt i32 {diff}, 0`)
let adiff = emit_bind(`select i1 {dc}, i32 {dneg}, i32 {diff}`)
let pos = emit_bind(`icmp sgt i32 {diff}, 0`)
let neg = emit_bind(`icmp slt i32 {diff}, 0`)
let slo = emit_bind(`select i1 {neg}, i32 -1, i32 0`)
let sgn = emit_bind(`select i1 {pos}, i32 1, i32 {slo}`)
let stepv = emit_bind(`mul i32 {sgn}, {d.code}`)
let moved = emit_bind(`add i32 {f.code}, {stepv}`)
let reach = emit_bind(`icmp sle i32 {adiff}, {d.code}`)
return val(emit_bind(`select i1 {reach}, i32 {to.code}, i32 {moved}`), "fixed")
}
if (meth == "smoothstep") { # smooth 0..1 ramp between e0 and e1
let e0 = emit_expr(e.kids[0]); let e1 = emit_expr(e.kids[1]); let x = emit_expr(e.kids[2])
let tt = fx_inv_lerp_code(e0.code, e1.code, x.code)
let c1 = emit_bind(`icmp slt i32 {tt}, 0`)
let t0 = emit_bind(`select i1 {c1}, i32 0, i32 {tt}`)
let c2 = emit_bind(`icmp sgt i32 {t0}, 65536`)
let t = emit_bind(`select i1 {c2}, i32 65536, i32 {t0}`)
let twot = emit_bind(`mul i32 {t}, 2`)
let poly = emit_bind(`sub i32 196608, {twot}`)
let tsq = fx_mul_code(t, t)
return val(fx_mul_code(tsq, poly), "fixed")
}
# remap(v, in0, in1, out0, out1) = lerp(out0, out1, inverse_lerp(in0, in1, v))
let v = emit_expr(e.kids[0])
let i0 = emit_expr(e.kids[1]); let i1 = emit_expr(e.kids[2])
let o0 = emit_expr(e.kids[3]); let o1 = emit_expr(e.kids[4])
let t = fx_inv_lerp_code(i0.code, i1.code, v.code)
return val(fx_lerp_code(o0.code, o1.code, t), "fixed")
}
# 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
# 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("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")
emith(" %np = alloca i64\n %rp = alloca i64\n %bp = alloca i64\n")
emith(" store i64 %n0, ptr %np\n store i64 0, ptr %rp\n store i64 4611686018427387904, ptr %bp\n br label %adj\n")
emith("adj:\n %b1 = load i64, ptr %bp\n %n1 = load i64, ptr %np\n %tb = icmp ugt i64 %b1, %n1\n br i1 %tb, label %adjb, label %loop\n")
emith("adjb:\n %b2 = lshr i64 %b1, 2\n store i64 %b2, ptr %bp\n br label %adj\n")
emith("loop:\n %b3 = load i64, ptr %bp\n %bz = icmp eq i64 %b3, 0\n br i1 %bz, label %done, label %body\n")
emith("body:\n %r1 = load i64, ptr %rp\n %n2 = load i64, ptr %np\n %rb = add i64 %r1, %b3\n %ge = icmp uge i64 %n2, %rb\n br i1 %ge, label %sub, label %shift\n")
emith("sub:\n %n3 = sub i64 %n2, %rb\n store i64 %n3, ptr %np\n %rsh = lshr i64 %r1, 1\n %rnew = add i64 %rsh, %b3\n store i64 %rnew, ptr %rp\n br label %next\n")
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(" %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("entry:\n")
emith(" %xz = icmp eq i32 %x, 0\n")
emith(" %yz = icmp eq i32 %y, 0\n")
emith(" %both0 = and i1 %xz, %yz\n")
emith(" br i1 %both0, label %z, label %go\n")
emith("z:\n")
emith(" ret i32 0\n")
emith("go:\n")
emith(" %yneg = icmp slt i32 %y, 0\n")
emith(" %yng = sub i32 0, %y\n")
emith(" %ay0 = select i1 %yneg, i32 %yng, i32 %y\n")
emith(" %ay = add i32 %ay0, 1\n")
emith(" %xpos = icmp sge i32 %x, 0\n")
emith(" br i1 %xpos, label %xp, label %xn\n")
emith("xp:\n")
emith(" %n1 = sub i32 %x, %ay\n")
emith(" %d1 = add i32 %x, %ay\n")
emith(" br label %dv\n")
emith("xn:\n")
emith(" %n2 = add i32 %x, %ay\n")
emith(" %d2 = sub i32 %ay, %x\n")
emith(" br label %dv\n")
emith("dv:\n")
emith(" %num = phi i32 [ %n1, %xp ], [ %n2, %xn ]\n")
emith(" %den = phi i32 [ %d1, %xp ], [ %d2, %xn ]\n")
emith(" %base = phi i32 [ 51472, %xp ], [ 154416, %xn ]\n")
emith(" %n64 = sext i32 %num to i64\n")
emith(" %nsh = shl i64 %n64, 16\n")
emith(" %d64 = sext i32 %den to i64\n")
emith(" %rdv = sdiv i64 %nsh, %d64\n")
emith(" %r = trunc i64 %rdv to i32\n")
emith(" %r64a = sext i32 %r to i64\n")
emith(" %r64b = sext i32 %r to i64\n")
emith(" %rr = mul i64 %r64a, %r64b\n")
emith(" %rrs = ashr i64 %rr, 16\n")
emith(" %r2 = trunc i64 %rrs to i32\n")
emith(" %r2e = sext i32 %r2 to i64\n")
emith(" %re = sext i32 %r to i64\n")
emith(" %r3m = mul i64 %r2e, %re\n")
emith(" %r3s = ashr i64 %r3m, 16\n")
emith(" %r3 = trunc i64 %r3s to i32\n")
emith(" %r3e = sext i32 %r3 to i64\n")
emith(" %c1 = mul i64 %r3e, 12865\n")
emith(" %c1s = ashr i64 %c1, 16\n")
emith(" %t1 = trunc i64 %c1s to i32\n")
emith(" %re2 = sext i32 %r to i64\n")
emith(" %c2 = mul i64 %re2, 64337\n")
emith(" %c2s = ashr i64 %c2, 16\n")
emith(" %t2 = trunc i64 %c2s to i32\n")
emith(" %poly = sub i32 %t1, %t2\n")
emith(" %angle = add i32 %poly, %base\n")
emith(" %angneg = sub i32 0, %angle\n")
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
# [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(" %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")
emith(" %m3 = mul i64 %fe, %p4\n %s3 = ashr i64 %m3, 16\n %p3 = add i64 %s3, 15744\n")
emith(" %m2 = mul i64 %fe, %p3\n %s2 = ashr i64 %m2, 16\n %p2 = add i64 %s2, 45426\n")
emith(" %m1 = mul i64 %fe, %p2\n %s1 = ashr i64 %m1, 16\n %p1 = add i64 %s1, 65536\n")
emith(" %p = trunc i64 %p1 to i32\n")
emith(" %ipos = icmp sge i32 %i, 0\n")
emith(" %sa0 = select i1 %ipos, i32 %i, i32 0\n %sahi = icmp sgt i32 %sa0, 30\n %sa = select i1 %sahi, i32 30, i32 %sa0\n")
emith(" %shl = shl i32 %p, %sa\n")
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
# 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("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")
emith(" %e = sub i32 %pmsb, 16\n %ef = shl i32 %e, 16\n")
emith(" %sh = sub i32 16, %pmsb\n %shpos = icmp sge i32 %sh, 0\n %nsh = sub i32 0, %sh\n")
emith(" %mL = shl i32 %x, %sh\n %mR = ashr i32 %x, %nsh\n %m = select i1 %shpos, i32 %mL, i32 %mR\n")
emith(" %num = sub i32 %m, 65536\n %den = add i32 %m, 65536\n")
emith(" %n64 = sext i32 %num to i64\n %nshl = shl i64 %n64, 16\n %d64 = sext i32 %den to i64\n %rdv = sdiv i64 %nshl, %d64\n %r = trunc i64 %rdv to i32\n")
emith(" %re = sext i32 %r to i64\n %rr = mul i64 %re, %re\n %r2 = ashr i64 %rr, 16\n")
emith(" %c3 = mul i64 %r2, 9362\n %c3s = ashr i64 %c3, 16\n %q3 = add i64 %c3s, 13107\n")
emith(" %c2 = mul i64 %r2, %q3\n %c2s = ashr i64 %c2, 16\n %q2 = add i64 %c2s, 21845\n")
emith(" %c1 = mul i64 %r2, %q2\n %c1s = ashr i64 %c1, 16\n %q1 = add i64 %c1s, 65536\n")
emith(" %rp = mul i64 %re, %q1\n %rps = ashr i64 %rp, 16\n %lnm = shl i64 %rps, 1\n")
emith(" %lm = mul i64 %lnm, 94548\n %lms = ashr i64 %lm, 16\n %l2m = trunc i64 %lms to i32\n")
emith(" %res = add i32 %ef, %l2m\n ret i32 %res\n}\n")
}