Files
UniXNU/scripts/check-metal-vulkan-probe.py

70 lines
4.2 KiB
Python

#!/usr/bin/env python3
"""Exercise real translator refusals; never opens a Vulkan device."""
import argparse
import hashlib
import json
from pathlib import Path
import subprocess
import tempfile
ROOT = Path(__file__).resolve().parents[1]
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--air-input-dir', required=True, type=Path)
parser.add_argument('--compiler-build', type=Path, default=ROOT / 'build/gpu-001/compiler')
args = parser.parse_args()
build = json.loads((args.compiler_build / 'build-report.json').read_text())
binary = Path(build['binary'])
if hashlib.sha256(binary.read_bytes()).hexdigest() != build['binary_sha256'] or \
hashlib.sha256((ROOT / 'drivers/AdrenoMetal/tools/MetalVulkanProbe.rs').read_bytes()).hexdigest() != build['source_sha256']:
raise SystemExit('Compiler identity mismatch')
profile = ROOT / '.cache/gpu-reference/metal2vulkan/crates/metal2vulkan-validation/profiles/vk12-bare.json'
vertex = args.air_input_dir / 'QuartzCore/module-7.bc'
fragment = args.air_input_dir / 'QuartzCore/module-65.bc'
layout = ROOT / 'drivers/AdrenoMetal/tools/probe-vertex-layout.json'
colors = ROOT / 'drivers/AdrenoMetal/tools/probe-coverage-attachment.json'
audit = json.loads((args.air_input_dir / 'target-inputs.json').read_text())
records = {(m['library'], m['index']): m for m in audit['modules']}
for path, index in [(vertex, 7), (fragment, 65)]:
if hashlib.sha256(path.read_bytes()).hexdigest() != records[('QuartzCore', index)]['bitcode_sha256']:
raise SystemExit('Original module hash mismatch')
vname, fname = (records[('QuartzCore', i)]['name'] for i in [7, 65])
with tempfile.TemporaryDirectory(prefix='controls-', dir=ROOT / 'build/gpu-001') as temporary:
work = Path(temporary)
malformed = work / 'malformed.bc'
malformed.write_bytes(b'BC\xc0\xde')
bad_profile = work / 'profile.json'
bad_profile.write_text('{}')
bad_layout = work / 'layout.json'
bad_layout.write_text(json.dumps(dict(layouts={}, attributes={
'0': dict(format='Float4', offset=0, buffer_index=0)})))
controls = [
('truncated-bitcode', ['--list', malformed]),
('invalid-profile', [bad_profile, vertex, vname]),
('missing-entry', [profile, vertex, '__absent_entry__']),
('standalone-fragment', [profile, fragment, fname]),
('wrong-fragment-stage', ['--vertex-descriptor', layout, profile, vertex, vname, vertex, vname]),
('missing-vertex-layout', ['--vertex-descriptor', bad_layout, profile, vertex, vname]),
('missing-color-attachment', ['--vertex-descriptor', layout, profile, vertex, vname, fragment, fname]),
('incompatible-varyings', ['--vertex-descriptor', layout, '--color-attachments', colors,
profile, vertex, vname, fragment, fname]),
]
for name, command in controls:
result = subprocess.run([str(binary), *map(str, command)], capture_output=True, text=True, timeout=20)
if result.returncode != 2 or result.stdout.strip() or not result.stderr.startswith('REFUSED:'):
raise AssertionError(f'{name}: failed closed-boundary check: {result.returncode}')
# A changed inventory must fail before any translation output is created.
(work / 'target-inputs.json').write_text((args.air_input_dir / 'target-inputs.json').read_text() + '\n')
result = subprocess.run(['python3', '-B', str(ROOT / 'scripts/probe-metal-vulkan-host.py'),
'--air-input-dir', str(work), '--compiler-build', str(args.compiler_build),
'--output', str(work / 'refused-output')], capture_output=True, text=True, timeout=20)
if result.returncode == 0 or (work / 'refused-output').exists() or 'pinned original inventory' not in result.stderr:
raise AssertionError('Changed target inventory was not refused before output')
print('PASS: eight actual parser/pipeline refusals and changed-inventory refusal; no GPU execution')
if __name__ == '__main__':
main()