Merge remote-tracking branch 'origin/linux' into linux

# Conflicts:
#	engine/rr/ptx2rr.py
#	install.sh
#	packaging/build_engine_models.sh
This commit is contained in:
rozaxlia committed 2026-10-19 21:13:16 +02:00
commit c0fed5e5cb
50 files changed
+1627 -238

No files matched your search

+37 -11
View File
@@ -139,31 +139,49 @@ void SA32(uint a, uint v) { CHECKS(a + 2u);
// UNORM 10:10:10:2 surfaces (the pooled histories) are RGBA32F images holding the quantised values, like ZLUDA's.
// tanh.approx.f32 as the reference computes it: ROCm's OCML tanh (device-libs ocml/src/tanhF.cl) with LLVM's AMDGPU
// lowering of exp (lowerFEXP, fast-FMA path): the same fused operations, hardware exp2, ldexp and reciprocal.
// (a fused f32 a * b + c through f64: the product is exact there and the sum is rounded to 53 bits before the f32
// rounding. The driver's own f32 fma is a product and a sum whenever its product also exists on its own, as in
// exp's ph / fma(x, c, -ph) pair.)
float fmad(float a, float b, float c) { precise double s = double(a) * double(b) + double(c); return float(s); }
// Up to 2026-10-19 every fused operation went through f64 (the product exact there, the sum rounded to 53 bits, then
// to f32), which this GPU runs at a small fraction of the f32 rate. They are the driver's f32 fma now, with two
// exceptions that keep every result bit for bit what it was, on all 2^32 arguments (RX 9070 XT, RADV; NaN payloads
// included):
// - the polynomial stays in f64: the driver drops the f32 rounding between nested f64 operations (it folds
// f2f64(f2f32(x)) to x; the conversions are not NoContraction), so its four nested steps always ran in f64 with one
// rounding at the end. In f32 (true fma), 24,836 arguments round one f32 step differently, 2 of them in binary16.
// - exp's error term fma(w, c, -ph) next to ph = w * c: the driver makes that the product it already has and a sum,
// exactly 0, even with every operand precise or opaque. It is Dekker's exact product error: w split into two halves
// of 12 bits (w * 4097 = w * (2^12 + 1)), log2(e) into 1.4423828125 + 0.0003122091293334961 (11 and 12 bits), so
// every partial product is exact and so is each sum.
// A NaN argument keeps its payload, quieted and positive, as the f64 conversion left it.
float tanh_ocml(float x)
{
const float y = abs(x);
precise float z;
if (y < 0.625) {
precise float y2 = y * y;
precise float p = fmad(y2, fmad(y2, fmad(y2, fmad(y2, -0.005700020585209131, 0.02063407190144062), -0.053737930953502655), 0.13331416249275208), -0.3333328068256378);
precise double y2d = double(y2);
precise double pd = (((y2d * -0.005700020585209131 + 0.02063407190144062) * y2d - 0.053737930953502655) * y2d +
0.13331416249275208) * y2d - 0.3333328068256378;
precise float p = float(pd);
precise float yp = y * p;
z = fmad(y2, yp, y);
z = fma(y2, yp, y);
} else {
const float w = 2.0 * y;
precise float ph = w * 1.4426950216293335;
precise float f0 = fmad(w, 1.4426950216293335, -ph);
precise float pl = fmad(w, 1.925962855864327e-08, f0);
precise float wt = w * 4097.0;
precise float wh = wt - (wt - w);
precise float wl = w - wh;
precise float f1 = wh * 1.4423828125 - ph;
precise float f2 = f1 + wh * 0.0003122091293334961;
precise float f3 = f2 + wl * 1.4423828125;
precise float f0 = f3 + wl * 0.0003122091293334961;
precise float pl = fma(w, 1.925962855864327e-08, f0);
const float e = roundEven(ph);
precise float d = ph - e;
precise float a = d + pl;
float t = ldexp(exp2(a), int(e));
if (w > 88.72283172607422) t = uintBitsToFloat(0x7f800000u);
precise float s1 = t + 1.0;
z = fmad(-2.0, rcp_a(s1), 1.0);
z = fma(-2.0, rcp_a(s1), 1.0);
if (isnan(z)) z = uintBitsToFloat(floatBitsToUint(y) | 0x00400000u);
}
return x < 0.0 || floatBitsToUint(x) == 0x80000000u ? -z : z;
}
@@ -1032,7 +1050,11 @@ class RRT(base.T):
w(f'shared uint mms[{384 * (self.bx * self.by * self.bz // 32)}]; // mma_e4m3_f16 scratch, per subgroup')
def runtime(self):
return (RR_RUNTIME + (ROOT / 'engine' / 'round_half.glsl').read_text() +
runtime = RR_RUNTIME
if getattr(self, 'unguarded', False): # --unguarded: no opaque zero
assert '#define SUST_Z (gl_NumWorkGroups.z - 1u)' in RR_RUNTIME
runtime = RR_RUNTIME.replace('#define SUST_Z (gl_NumWorkGroups.z - 1u)', '#define SUST_Z 0u')
return (runtime + (ROOT / 'engine' / 'round_half.glsl').read_text() +
(ROOT / 'engine' / 'cnn_sr' / 'mma_f16.glsl').read_text())
@@ -1061,6 +1083,9 @@ def main():
ap.add_argument('--oob-report', action='store_true', help='debugging: count out-of-range buffer accesses')
ap.add_argument('--exact-convert', action='store_true',
help='the module goes through spv_exact_convert.py: lighter guards on binary16 conversions (CVT_GUARD)')
ap.add_argument('--unguarded', action='store_true',
help="none of the exactness guards: no 'precise' registers and no opaque zero OR-ed into binary16 "
"stores and conversions, so the driver may contract, fold and fuse (compile_rr2.py D4R_RR2_UNGUARDED)")
ns = ap.parse_args()
block = tuple(int(x) for x in ns.block.split(',')) if ns.block else default_block(ns.kernel)
formats = {int(k): v for k, v in (x.split('=') for x in ns.surface)}
@@ -1077,8 +1102,9 @@ def main():
t.probes = probes
t.probe_block = tuple(int(x) for x in ns.probe_block.split(','))
t.oob = ns.oob_report
t.unguarded = ns.unguarded
return t
t = base.build(make, False, False, False)
t = base.build(make, False, ns.unguarded, False)
for off, fmt in formats.items():
if off in t.surfaces:
t.surfaces[off] = fmt