nocontract.py and run-lavapipe.sh: the strict-form gate under GLSL_DIR (-Gis, NoContraction on both modules)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
7b121f6ab2
commit
38668b6ec0
2 changed files with 46 additions and 2 deletions
34
packages/cultmath/tools/device/nocontract.py
Normal file
34
packages/cultmath/tools/device/nocontract.py
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#!/usr/bin/env python3
|
||||
"""The strict-form harness pass of the GLSL equality gate: decorates the result of every OpFMul, OpFAdd, OpFSub,
|
||||
OpVectorTimesScalar and OpDot NoContraction, so the driver may not fuse them into a multiply-add. Binary, instruction
|
||||
level, reusing spvedit.py's load and mk. It lives in the gate's job-local modules only, never in shipped GLSL.
|
||||
|
||||
usage: nocontract.py <in.spv> <out.spv> prints the count of decorated results"""
|
||||
import importlib.util, os, struct, sys
|
||||
|
||||
here = os.path.dirname(os.path.abspath(__file__))
|
||||
spec = importlib.util.spec_from_file_location('spvedit', os.path.join(here, '..', 'glsl', 'spvedit.py'))
|
||||
spvedit = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(spvedit)
|
||||
|
||||
OP_DOT, OP_FADD, OP_FSUB, OP_FMUL, OP_VECTOR_TIMES_SCALAR = 148, 129, 131, 133, 142
|
||||
FLOAT_OPS = (OP_FADD, OP_FSUB, OP_FMUL, OP_VECTOR_TIMES_SCALAR, OP_DOT)
|
||||
DECORATION_NO_CONTRACTION = 42
|
||||
FIRST_TYPE_OP, LAST_TYPE_OP = 19, 39 # OpTypeVoid .. OpTypeForwardPointer: the annotations end where types begin
|
||||
|
||||
|
||||
def main(src, dst):
|
||||
h, ins = spvedit.load(src)
|
||||
done = {x[1] for x in ins if spvedit.op(x) == spvedit.OP_DECORATE and x[2] == DECORATION_NO_CONTRACTION}
|
||||
ids = [x[2] for x in ins if spvedit.op(x) in FLOAT_OPS and x[2] not in done]
|
||||
at = next((k for k, x in enumerate(ins) if FIRST_TYPE_OP <= spvedit.op(x) <= LAST_TYPE_OP), None)
|
||||
if at is None:
|
||||
sys.exit('refuse: no type section to place the decorations before')
|
||||
ins[at:at] = [spvedit.mk(spvedit.OP_DECORATE, i, DECORATION_NO_CONTRACTION) for i in ids]
|
||||
words = h + [v for x in ins for v in x]
|
||||
open(dst, 'wb').write(struct.pack('<%dI' % len(words), *words))
|
||||
print('nocontract: %d results decorated' % len(ids))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(*sys.argv[1:3])
|
||||
|
|
@ -7,7 +7,9 @@
|
|||
# SUMMARY=<log> tools/stopgap/ygg-verify.sh <repo> <rev> mcr.microsoft.com/dotnet/sdk:10.0 'bash packages/cultmath/tools/device/run-lavapipe.sh'
|
||||
# GLSL_DIR=<dir holding core.glsl, cycles.glsl, phacelle.glsl> (the output of tools/glsl/build-glsl.sh) runs the built
|
||||
# route instead of the committed one and skips the bounds judge: every lavapipe-glsl/<tag>.out must equal its
|
||||
# lavapipe-hlsl/<tag>.out byte for byte, and a difference exits 1. It records no bounds.
|
||||
# lavapipe-hlsl/<tag>.out byte for byte, and a difference exits 1. It records no bounds. That compare is in strict form:
|
||||
# dxc gets -Gis and nocontract.py decorates every float result of both modules NoContraction, so the two routes cannot
|
||||
# differ by a fused multiply-add.
|
||||
# WRITE_BOUNDS=1 re-records packages/cultmath/tests/CultMath.Tests/fixtures/device-bounds-lavapipe.txt and prints it
|
||||
# between BOUNDS-BEGIN and BOUNDS-END, because the job's clone is discarded.
|
||||
set -euo pipefail
|
||||
|
|
@ -38,12 +40,19 @@ export CULTMATH_DEVICE_WORK=$W/work
|
|||
dotnet test "$TESTS" --no-build --filter "FullyQualifiedName~GlslMirrorTests.WriteDeviceWork"
|
||||
python3 "$DEV/gen.py" "$W/work" "$CM"
|
||||
|
||||
STRICT=(); [ -z "${GLSL_DIR:-}" ] || STRICT=(-Gis)
|
||||
while IFS=$'\t' read -r tag _ _ n _ wout; do
|
||||
in=$W/work/$tag.in
|
||||
"$DXC" -T cs_6_0 -E main -HV 2021 -spirv -fspv-target-env=vulkan1.2 -I "$CM/shaders" "$W/work/$tag.hlsl" -Fo "$W/work/$tag.h.spv" >"$W/work/$tag.h.log" 2>&1 \
|
||||
"$DXC" -T cs_6_0 -E main -HV 2021 "${STRICT[@]}" -spirv -fspv-target-env=vulkan1.2 -I "$CM/shaders" "$W/work/$tag.hlsl" -Fo "$W/work/$tag.h.spv" >"$W/work/$tag.h.log" 2>&1 \
|
||||
|| { echo "dxc failed for $tag"; head -5 "$W/work/$tag.h.log"; exit 1; }
|
||||
glslangValidator -V --target-env vulkan1.2 -o "$W/work/$tag.g.spv" "$W/work/$tag.comp" >"$W/work/$tag.g.log" 2>&1 \
|
||||
|| { echo "glslang failed for $tag"; head -5 "$W/work/$tag.g.log"; exit 1; }
|
||||
if [ -n "${GLSL_DIR:-}" ]; then
|
||||
for m in h g; do
|
||||
python3 "$DEV/nocontract.py" "$W/work/$tag.$m.spv" "$W/work/$tag.$m.nc.spv" >>"$W/nocontract.txt"
|
||||
mv "$W/work/$tag.$m.nc.spv" "$W/work/$tag.$m.spv"
|
||||
done
|
||||
fi
|
||||
"$W/vkrun" "$W/work/$tag.h.spv" "$in" "$W/results/lavapipe-hlsl/$tag.out" "$n" "$wout" >>"$W/device.txt"
|
||||
"$W/vkrun" "$W/work/$tag.g.spv" "$in" "$W/results/lavapipe-glsl/$tag.out" "$n" "$wout" >>"$W/device.txt"
|
||||
done <"$W/work/manifest.txt"
|
||||
|
|
@ -54,6 +63,7 @@ if [ -n "${GLSL_DIR:-}" ]; then
|
|||
tag=$(basename "$h" .out); total=$((total + 1))
|
||||
if cmp -s "$h" "$W/results/lavapipe-glsl/$tag.out"; then equal=$((equal + 1)); else echo "differs: $tag"; fi
|
||||
done
|
||||
echo "strict form: $(wc -l <"$W/nocontract.txt") modules rewritten, $(awk '{n += $2} END {print n}' "$W/nocontract.txt") results decorated NoContraction"
|
||||
echo "lavapipe-glsl equals lavapipe-hlsl: $equal/$total"
|
||||
[ "$equal" = "$total" ] && [ "$total" -gt 0 ]
|
||||
exit
|
||||
|
|
|
|||
Loading…
Reference in a new issue