Skip to content

feat(windows): enable native HIP with deterministic runtime binding - #788

Open
Kenneth-Javier wants to merge 15 commits into
JustVugg:devfrom
Kenneth-Javier:feature/windows-hip-native
Open

feat(windows): enable native HIP with deterministic runtime binding#788
Kenneth-Javier wants to merge 15 commits into
JustVugg:devfrom
Kenneth-Javier:feature/windows-hip-native

Conversation

@Kenneth-Javier

@Kenneth-Javier Kenneth-Javier commented Aug 2, 2026

Copy link
Copy Markdown

Summary

Adds a native Windows HIP path so AMD GPUs are reachable from the Windows build:

  • HIP_DLL=1 builds c/coli_hip.dll from the existing
    c/backend_cuda.cu and existing coli_cuda_* ABI. No new backend abstraction
    is introduced, and the Linux HIP path remains unchanged.
  • The host loads that DLL from its own directory by absolute path, using the
    same seam as the existing Windows CUDA split.
  • Deterministic, fail-closed HIP runtime binding.
    COLI_HIP_RUNTIME_DIR names the directory containing amdhip64_7.dll; the
    loader refuses to start the GPU tier if a different runtime is already
    mapped, if more than one matching runtime is mapped, or if it cannot establish
    physical file identity.
  • 78 no-GPU loader-contract tests, driven by synthetic stub DLLs, now run on
    Windows CI.
  • The path was validated on physical gfx1151 hardware.
  • Teacher forcing was token-exact against the CPU path at the token-ID level:
    both CPU runs and both hybrid-HIP runs selected the same predicted token at
    every one of the 32 checked positions.
  • CPU and hybrid-HIP free decoding also produced the same exact 20-token
    autoregressive sequence across two independent processes per mode.

Why

HIP=1 is Linux-only and is refused elsewhere by an early Makefile error. On
Windows, MinGW GCC cannot compile .cu files, while hipcc targets the MSVC
ABI. There was therefore no native route from the Windows build to an AMD GPU;
the Windows host could select only the CUDA DLL.

The DLL split alone is insufficient because AMD ships a versioned runtime,
and Windows resolves an imported DLL basename against modules already loaded in
the process. A machine may contain several copies of amdhip64_7.dll, such as a
system-wide ROCm installation, an SDK runtime, an unpacked development build, or
a copy in a system search location.

Silently binding to an ambient System32 or unrelated ROCm runtime is not an
acceptable failure mode for a GPU backend. This implementation therefore
selects the runtime explicitly and verifies the resulting module identity.

Implementation

Native Windows HIP build

HIP_DLL=1 is opt-in. The default build remains CPU-only.

The HIP DLL build supports:

  • HIP_DLL=1
  • an explicit HIP_ARCH
  • HIP_SDK_ROOT
  • HIP_BIN_DIR
  • HIP_INCLUDE_DIR
  • HIP_LIB_DIR
  • HIP_DEVICE_LIB_PATH
  • HIPCC

HIP_SDK_ROOT may default from HIP_PATH, but every major SDK component can be
overridden independently because packaged SDK layouts do not necessarily keep
runtime, development, LLVM, and device-library files under one common root.

HIP_ARCH must be explicit on native Windows because
rocm_agent_enumerator is not available there.

CUDA_DLL=1 and HIP_DLL=1 are mutually exclusive.

The existing Linux HIP and Windows CUDA build paths remain supported.

Backend selection

A Windows host built with HIP_DLL=1 selects coli_hip.dll rather than
coli_cuda.dll.

The backend DLL is loaded from the host executable's directory by absolute
path. The existing coli_cuda_* ABI remains the host/backend seam, so no
parallel HIP-specific backend interface is introduced.

Deterministic runtime binding

For HIP DLL mode:

  • COLI_HIP_RUNTIME_DIR identifies the directory containing
    amdhip64_7.dll.
  • The configured path must be absolute.
  • Existing same-basename runtime modules are enumerated before initialization.
  • Runtime modules are compared by physical file identity, using volume and
    file index rather than path text alone.
  • An alternate spelling or hard link to the same physical file is accepted.
  • A different physical amdhip64_7.dll is rejected.
  • More than one mapped same-basename runtime is rejected, even if one is the
    configured runtime.
  • Failure to enumerate modules or establish file identity is treated as an
    initialization failure.
  • The configured runtime is loaded by absolute path.
  • Runtime identity is verified again after the backend is loaded.
  • Backend initialization fails closed if any binding condition is violated.
Situation Behaviour
A different amdhip64_7.dll is already loaded Refuse
More than one same-basename runtime is loaded Refuse
Module inventory cannot be read Refuse
Physical identity cannot be established Refuse
Configured path is relative or drive-relative Refuse
Configured runtime file is absent Refuse
Exactly one matching configured runtime is loaded Proceed

There is no bare-name fallback and no implicit System32 runtime search.

The runtime is verified after it is mapped. LoadLibraryExW accepts a path,
not an already-open file handle, so a file replacement between preliminary
validation and loading is detected afterward rather than prevented beforehand.

CUDA_DLL=1 hosts ignore COLI_HIP_RUNTIME_DIR and preserve their existing
coli_cuda.dll behaviour.

Diagnostics

Windows backend-load failures now distinguish relevant failure classes,
including:

  • backend file not found
  • missing transitive dependency
  • invalid or incompatible image
  • configured HIP runtime unavailable
  • conflicting runtime already loaded
  • duplicate same-basename runtimes
  • runtime identity mismatch

This replaces a generic failure message that could not reliably identify what
had gone wrong.

Tests

c/tests/test_backend_loader.py is the dedicated owner for the Windows
host/backend loading contracts.

Its synthetic DLL fixtures cover:

  • CUDA and HIP host selection
  • explicit HIP runtime-directory requirements
  • absolute-path validation
  • runtime and backend path selection
  • wrong-runtime rejection
  • duplicate same-basename runtime detection
  • hard-link and physical-identity behaviour
  • Unicode paths
  • paths containing spaces
  • missing backend files
  • missing dependencies
  • bad images
  • truthful diagnostics
  • same-process binding behaviour
  • controlled initialization failures
  • shutdown behaviour
  • isolation from real GPU hardware and installed runtimes

The suite does not require ROCm, TheRock, CUDA, a physical GPU, or a real
amdhip64_7.dll.

Validation

Static and regression validation

Suite Result
c/tests/test_backend_loader.py 78 passed, 0 failures, 0 errors, 0 skips
Full Python suite (unittest discover -s tests) 344 passed, 0 failures, 0 errors, 37 skipped
Full C suite before the later upstream rebase 37 binaries, 0 compile errors, runner exit 0
Full C suite after the later upstream rebase 39 binaries, 0 compile errors, runner exit 0

The original 37 Python skips were environmental, including unavailable optional
Python packages, binaries not built for that test run, and POSIX-only script
coverage. None was loader-, HIP-, or backend-contract-related.

Physical Windows HIP validation

Validated configuration:

  • AMD Ryzen AI Max+ 395
  • AMD Radeon(TM) 8060S Graphics
  • runtime-reported architecture: gfx1151
  • TheRock HIP runtime: 7.14.60850
  • Visual Studio 2022 MSVC: 14.44.35207
  • Windows SDK: 10.0.26100.0

Measured results:

  • Zero matching HIP runtime modules before initialization.
  • Exactly one matching runtime afterward, at the explicitly configured
    C:\TheRock\build\bin\amdhip64_7.dll.
  • Loaded runtime physical identity matched the configured file.
  • Separate System32 and ROCm 7.1 copies on the same machine were not selected.
  • The runtime module count returned to zero after explicit probe shutdown.
  • The physical Radeon 8060S was reported as gfx1151.
  • A real quant_matmul kernel was executed through the production
    host/backend seam.
  • Two deterministic matrix cases both had maximum absolute error 0 against
    the CPU reference.
  • CPU teacher forcing scored 32/32 in two independent processes.
  • Hybrid Windows HIP teacher forcing scored 32/32 in two independent
    processes.
  • Teacher forcing was token-exact between the CPU and hybrid-HIP paths at the
    token-ID level
    .
  • The predicted token ID matched position-by-position at all 32 checked
    positions in both CPU runs and both hybrid-HIP runs.
  • All four ordered prediction sets were identical, with empty mismatch lists.
  • This was an exact comparison of selected token decisions, not a close or
    tolerance-based comparison.
  • The teacher-forcing result does not claim bit-identical logits.
  • Two CPU and two hybrid-HIP free-decode processes produced the same exact
    20-token sequence.
  • Prefix equality was independently checked at 1, 2, 4, 8, 16, and 20
    positions.
  • 46 dense tensors were resident on HIP in every hybrid model process.
  • Routed experts remained on CPU.
  • No disabled-tensor fallback or HIP-refusal diagnostic occurred in the
    qualified hybrid runs.
  • Exactly one matching HIP runtime was observed in each hybrid process.

The model workload was a small synthetic GLM MoE:

  • model type: glm_moe_dsa
  • 5 layers
  • 8 routed experts
  • top-2 routing
  • 140 tensors
  • generated offline from a fixed seed
  • no model-weight download required

The model is intended as a deterministic correctness fixture rather than a
production-model claim.

Post-rebase validation

The physical validation above was performed while the branch was based on
upstream dev at 72ddb673.

Upstream subsequently advanced through 44d7958 to 7fb1159, and this branch
was rebased onto the newer base.

The first rebase required one manual conflict resolution in c/Makefile:

  • upstream's new tests/test_kv_prefix target was preserved
  • this branch's hip-dll .PHONY entry was preserved

The resolution was the direct union of the two changes. No semantic alternative
had to be selected.

A later rebase from 44d7958 to 7fb1159 was conflict-free.

The first hosted Windows CI run also exposed two test-harness failures caused by
mixed path-separator conventions in the same process:

.../empty dir\amdhip64_7.dll
.../empty dir/amdhip64_7.dll

The loader continues to emit the correct native Windows \ separator.

The fix was limited to the test and CI harness:

  • both path values are normalized before comparison
  • the loader was not changed
  • the earlier os.sep CI assertion was removed because it encoded an incomplete
    diagnosis
  • the two security-relevant missing-runtime and identity assertions remain
    strict about path identity rather than textual separator spelling

The resulting branch head is:

1f76e498cc346a0c324a946711ea799ef953250a

on upstream base:

7fb11595245b6f320b3cfef9565fdbe6da92442b

Post-rebase and post-fix local validation completed with:

  • backend-loader contract suite: 78/78, 0 skips
  • loader suite exercised under both tested separator environments: 78/78
    in each
  • full C suite: 39 test binaries, runner exit 0
  • clean merge-tree against the selected dev base
  • 15 commits
  • 0 merge commits

The physical gfx1151 model validation was not rerun on head 1f76e498.

No W1-authored production patch changed after the physical validation. The later
change was limited to test path normalization and the corresponding CI
diagnostic logic, while newer upstream production changes were incorporated
through the rebase.

Evidence provenance

Three harness attempts preceded the authoritative model sweep and failed
before producing qualification data:

  • an empty model-path variable
  • a result-echo modification aimed at the wrong launcher
  • incorrect quoting generated between Python and PowerShell

None produced an authoritative process-result row, and their temporary logs
were not retained.

The retained qualification data consists of exactly eight final model
processes in one contiguous, correctly ordered sweep:

  • CPU teacher forcing ×2
  • hybrid HIP teacher forcing ×2
  • CPU free decode ×2
  • hybrid HIP free decode ×2

Every reported model comparison derives only from those eight final processes.
This is an evidence-provenance limitation, not a runtime or model failure.

CI boundary

The existing windows-latest leg of engines-all-platforms runs the synthetic
backend-loader contract suite.

The Windows runner may expose both separator conventions within the same
process: a temporary root may use forward slashes while the native loader
appends the runtime basename with a backslash.

The loader continues to emit the correct Windows separator. The tests normalize
both values before comparison instead of requiring identical textual spelling.

The CI step:

  • uses shell: pwsh
  • makes the required MSYS2 UCRT64 compiler tools available on PATH
  • also includes the MSYS2 usr\bin tools required by the fixture build
  • reports the active Python executable and relevant path environment
  • removes GPU and runtime selection variables
  • fails on any test failure or error
  • also fails if any loader test is skipped

The skip check is important because an incomplete fixture toolchain can
otherwise cause a test class to skip while unittest still reports an overall
successful exit.

This PR does not add hosted physical Windows HIP coverage. The existing
hosted CI configuration has neither the validated Windows HIP SDK setup nor a
physical AMD GPU runner.

engine-hip-syntax therefore continues to cover the Linux HIP compile path,
while the Radeon 8060S / gfx1151 results above were produced externally on
physical hardware and are reported as validation evidence rather than hosted CI
coverage.

Limitations

  • This is not full-GPU MoE inference. Routed experts remained on CPU.
  • Routed experts become GPU-eligible only through the existing expert-placement
    controls, including CUDA_EXPERT_GB and an appropriate pin or usage source.
    That configuration was not validated here.
  • Validation used a small synthetic GLM fixture, not a production model.
  • Teacher-forcing equivalence is a claim of identical selected token IDs at all
    32 checked positions, not bit-identical logits.
  • The canonical oracle path does not provide arbitrary generation-length
    control.
  • No 32-, 64-, or 100-token free-decode claim is made. The validated
    autoregressive decode length is 20 positions.
  • The decode path was exercised recursively for 20 positions without
    divergence. This is not presented as a complete isolated KV-cache proof.
  • Normal one-shot model exits rely on Windows process teardown rather than an
    explicit coli_cuda_shutdown call.
  • That lifecycle behaviour is shared with the existing host path and is not
    presented as explicit backend shutdown.
  • Per-tensor upload failure may fall back to CPU while the command still exits
    successfully.
  • A run may print a [CUDA] device 0: ... line while having no useful GPU tensor
    residency.
  • [CUDA] resident set: N tensors with N > 0 is the useful-work signal; the
    device line alone is insufficient.
  • No performance, throughput, latency, or memory-scaling claims are made.
  • Validation covered one GPU, one HIP SDK configuration, and one MSVC toolchain.
  • In the tested configuration, MSVC 14.51.36231 failed because its <cmath>
    declarations collided with HIP's __device__ overloads.
  • The validated build therefore pins VCToolsVersion=14.44.35207.
  • This does not claim that every future HIP/MSVC combination based on 14.51
    will fail.
  • The physical model results were produced before the branch was rebased onto
    dev at 7fb1159.
  • Post-rebase validation covered the loader contracts and complete C suite, but
    the physical model workload was not rerun on current head 1f76e498.
  • No compatibility claim is made for upstream commits after the selected dev
    base used by the current branch.

Manual operator test

No separate manual end-user/operator walkthrough was performed. The
application was nevertheless executed repeatedly through automated full-host
validation on the physical Windows machine, including model loading, teacher
forcing, and free autoregressive decoding.

Notes for reviewers

The series contains fifteen commits and keeps them separate intentionally:

  • Eleven commits introduce the build support, host/backend selection,
    deterministic runtime binding, diagnostics, synthetic fixtures, loader
    contracts, and supporting tooling incrementally.
  • Three commits are documentation and CI-only:
    • GPU_BACKENDS.md
    • docs/windows.md
    • .github/workflows/ci.yml
  • The fifteenth commit is the focused test/CI correction prompted by the first
    hosted Windows run:
    • normalize the two affected path comparisons
    • remove the incorrect os.sep CI guard
    • leave the production loader unchanged

No W1-authored production source changed after the physical hardware and
full-model validation.

Two PRs touch adjacent areas and may be useful context for maintainers:

Neither implementation was contained in the original selected base for this
branch.

Issue #759 is relevant background for integrated-GPU owners. HIP already
reports integrated=1 for the validated device, so this change does not alter
that classification.

@Kenneth-Javier
Kenneth-Javier marked this pull request as ready for review August 2, 2026 20:12
@JustVugg JustVugg added cuda Backend CUDA/NVIDIA feature Nuova funzionalità labels Aug 2, 2026
@JustVugg

JustVugg commented Aug 2, 2026

Copy link
Copy Markdown
Owner

CI approved and running — it was stuck in action_required, which GitHub applies to a first contribution from a fork. Nothing you did; sorry it sat.

Read the description properly and there is a lot here I like, so let me say what stands out before the review itself.

You reused the existing seam instead of inventing one. HIP_DLL=1 builds from the same backend_cuda.cu and the same coli_cuda_* ABI, through the same host-side split the Windows CUDA path already uses. No new backend abstraction. That is the difference between a contribution that adds a platform and one that adds a platform and a fork in the code everyone else then maintains.

Fail-closed runtime binding. Refusing to start the GPU tier when a different amdhip64_7.dll is already mapped, when more than one is, or when file identity cannot be established — that is the right default, and it is the opposite of what this project has been shipping. Two of today's issues were exactly the other failure mode: #783, where make kimi_k3 CUDA=1 compiled -DCOLI_CUDA and linked cudart on a source with no CUDA in it, and reported success at every observable point while the GPUs stayed idle. Silent wrong-thing is worse than a loud refusal.

78 loader-contract tests with synthetic stub DLLs, running on CI without a GPU. That is the part I would not have thought to ask for. Most GPU PRs here can only be verified by whoever owns the silicon; you made most of yours checkable by anyone.

What I will be reading for

Not a rubber stamp — 4,273 lines touching the build and the loader:

  • backend_cuda.cu shared with CUDA. Anything that compiles differently under hipcc has to leave the NVIDIA path byte-identical. The HIP syntax job covers compilation; I want to see the guards.
  • c/MakefileHIP=1 is Linux-only and refused early elsewhere. I want to check HIP_DLL=1 cannot be reached in a state that half-configures the build, which is the shape of [Bug]: Nvidia GPU's not working with Kimi K3 #783.
  • backend_loader.c — the absolute-path load and the identity check are the security-relevant part. Loading a DLL from the host's own directory by absolute path is right; I want to confirm there is no path where a relative or search-order load survives.
  • .github/workflows/ci.yml — how much runner time the 78 tests add, and whether they can fail for reasons unrelated to the loader.

One question

You validated on physical gfx1151 including teacher forcing and free decode. Was the teacher-forced run token-exact against the CPU path, or close-but-not-identical? The distinction matters here more than the throughput: this project makes no promise about speed and a hard one about semantics, so a backend that changes which tokens come out is opt-in by policy (that is what #763 was about, merged today).

If it is token-exact, say so in the PR body — it is the strongest claim you can make and it is currently not stated.

@JustVugg JustVugg added vulkan Backend Vulkan/AMD and removed vulkan Backend Vulkan/AMD labels Aug 2, 2026
@JustVugg

JustVugg commented Aug 2, 2026

Copy link
Copy Markdown
Owner

CI ran (it was held in action_required — a first contribution from a fork, and nothing surfaces that on the PR page). 349 of 351 pass. Two fail, on Windows only, and both are the same one-character thing:

FAIL test_configured_runtime_foundation
FAIL test_configured_runtime_reports_a_missing_runtime

'…/coli helper yhk99_d1/empty dir\amdhip64_7.dll'
                                 ^
'…/coli helper yhk99_d1/empty dir/amdhip64_7.dll'

The loader joins with \, the expectation is built with /. Under MSYS2 both conventions are live in the same process — the temp root arrives as D:/a/_temp/msys64/tmp/... with forward slashes while os.path.join gives a backslash — so a string comparison of paths cannot hold. I would not change the loader for this: \ is correct for Windows. Compare with os.path.normcase(os.path.normpath(...)), or pathlib.PurePath, on both sides.

Worth noting the two tests that failed are exactly the ones checking that a missing runtime is reported and that identity resolution is sound — the security-relevant pair. Good place for the harness to be strict; it just needs to be strict about the right thing.

Also now CONFLICTING — five PRs merged to dev tonight, including #785 which touched c/Makefile and c/coli. A rebase on current dev and the path fix should take it green.

Everything I said in the earlier comment stands, and the failure does not touch any of it: the fail-closed binding, the reuse of the existing coli_cuda_* seam and the 78 no-GPU loader tests are the parts that make this reviewable at all, and 349/351 on the first CI run of a 4,273-line PR is a good showing.

The question from before is still the one I care most about: was the teacher-forced gfx1151 run token-exact against the CPU path? If yes, put it in the PR body — it is the strongest claim available here and it is not currently stated.

@JustVugg JustVugg added the needs-rebase Confligge, serve rebase dell'autore label Aug 2, 2026
@Kenneth-Javier

Copy link
Copy Markdown
Author

Yes — it was token-exact against the CPU path at the token-ID level.

In the retained authoritative sweep, CPU teacher forcing ran twice and hybrid
HIP teacher forcing ran twice. All four runs scored 32/32, and the predicted
token ID matched position-by-position between the CPU and hybrid-HIP paths for
all 32 checked positions.

This was not a close or tolerance-based comparison. I am not claiming
bit-identical logits, only identical token decisions at every checked position.

Separately, the free autoregressive decode also produced the same exact
20-token sequence across two CPU processes and two hybrid-HIP processes.

I updated the PR body to make the teacher-forcing token-exactness explicit.

@Kenneth-Javier
Kenneth-Javier force-pushed the feature/windows-hip-native branch from 57145e5 to e5ff643 Compare August 2, 2026 22:51
@Kenneth-Javier

Copy link
Copy Markdown
Author

Post-rebase validation

After the physical validation above, upstream dev advanced from 72ddb673
to 44d7958, and this branch was rebased onto that newer base.

The only manual conflict resolution was in c/Makefile, where upstream's new
tests/test_kv_prefix target and this branch's hip-dll .PHONY entry were
both preserved. The remaining commits were range-diff identical.

Post-rebase validation on head e5ff643 completed with:

  • backend-loader contract suite: 78/78, 0 skips
  • full C suite: 39 test binaries, runner exit 0

The physical gfx1151 model results reported above were produced before this
rebase and were not rerun on e5ff643.

Add a portable native Windows build path for the HIP GPU backend while
keeping the default CPU build and the host-only HIP_DLL mode independent
of the HIP SDK.

- build backend_cuda.cu as coli_hip.dll for explicit gfx targets
- select the SDK through HIP_SDK_ROOT with component-level overrides
- preserve the existing coli_cuda_* backend ABI
- keep HIP_DLL host builds free of amdhip64 and HIP SDK requirements
- adapt TEMP validation to the MSVC CRT
- clean and ignore generated HIP DLL artifacts
- document the experimental build workflow and runtime limitations
- repair the unmatched Windows documentation fence so GPU sections render

Runtime loading of coli_hip.dll is intentionally deferred. The current
loader still selects coli_cuda.dll, and deterministic amdhip64_7.dll
resolution is part of the subsequent runtime-loader work.

Validated on Windows 11 with Radeon 8060S gfx1151, a Windows HIP SDK and
MSVC 14.44:

- PE32+ x64 DLL with embedded gfx1151 device code
- exact 48-to-48 export and loader-contract equality
- only amdhip64_7.dll and KERNEL32.dll imported
- no embedded absolute SDK path
- 36 C test binaries passed
- 273 Python tests passed, with 40 skipped

Windows HIP does not currently have hosted CI coverage.
Select the Windows backend DLL name and loader diagnostic label from
COLI_HIP_DLL so HIP hosts seek coli_hip.dll while CUDA hosts retain their
existing behaviour.

- derive bounds, copying and both load attempts from one filename constant
- emit HIP-specific loader diagnostics in HIP mode
- preserve byte-identical CUDA loader diagnostics
- retain the shared coli_cuda_* backend ABI
- strengthen the existing CUDA environment test owner with CPU-only
  subprocess coverage for both DLL modes

Deterministic amdhip64_7.dll selection and runtime-path verification remain
deferred to the next loader slice.
The engine enters its CUDA block only for a truthy COLI_CUDA, so an unset
variable means the GPU was never requested: no backend load, no diagnostic,
CPU path. Two tests claimed otherwise and both were unreachable rather than
merely stale. One asserted "auto-detect: backend unavailable", a string git
history shows was introduced by the test itself and has never existed in
production. The other asserted "[CUDA] mode:" with COLI_CUDA unset, although
that line is emitted only when the backend was explicitly enabled.

The Windows auto-enable they described belongs to the separate coli launcher,
which sets COLI_CUDA=1 before starting the engine.

- state the opt-in contract instead of auto-detect throughout the module
- scrub COLI_CUDA, COLI_GPU and COLI_GPUS from child environments
- assert no CUDA diagnostic, loader miss or mode line on the unset path
- prove execution reaches model validation instead of failing in GPU setup
- correct the GPU-present scenario so hardware alone does not opt in
- add one non-skipping isolated CUDA_DLL-host contract test

Both historical tests still skip without a CUDA host, so the isolated host
test carries the contract on machines that have none.

No production behaviour changes.
test_cuda_env.py had grown to cover two unrelated contracts: the COLI_CUDA
environment semantics it was written for and the backend-DLL selection added
for Windows HIP. Split the second into its own module so each file states one
thing and the loader tests no longer pay for the other module's import-time
probe of the resident colibri binary.

- move LoaderBackendSelectionTest to test_backend_loader.py
- preserve every test name, executable code line and assertion
- retain isolated CUDA_DLL and HIP_DLL host builds and sandboxes
- keep test_cuda_env.py focused on COLI_CUDA startup semantics
- use private fixture helpers instead of importing a module with import-time
  binary and GPU probes
- remove the now-unused shutil import from test_cuda_env.py

No production behaviour or test semantics change. The suite remains at 277
Python tests with 40 skipped, split as seven environment tests and four loader
tests for the extracted owner.
Add CPU-only Windows DLL fixtures that model two distinguishable HIP runtimes
and one complete fake HIP backend without requiring ROCm, a HIP SDK or GPU
hardware.

- derive the current 48-symbol backend ABI directly from backend_loader.c
- build runtime A and runtime B with the production amdhip64_7.dll basename
- link a fake coli_hip.dll against the runtime marker import
- export the complete coli_cuda_* ABI plus one test-only bound-runtime accessor
- inspect PE architecture, imports and exports with the existing MSYS2 objdump
- build everything under unique temporary paths containing spaces
- verify explicit cleanup leaves no generated source or binary in the repo

The fixtures are structural in this slice: no DLL is loaded and no behavioural
runtime-marker claim is made. A native same-process harness will exercise the
binding contract separately.

No production behaviour changes and no new toolchain installation is required.
Add a native Windows test harness that compiles the real backend_loader.c
unchanged and exercises it in a controlled subprocess with distinguishable
amdhip64_7.dll stubs.

- preload runtime A or runtime B from an absolute Unicode path
- inventory loaded same-basename runtime modules at each loader stage
- verify the preload marker, loaded path and backend-bound marker agree
- prove an already loaded runtime B satisfies a backend linked against A
- classify the ambient no-preload result without assuming System32 behaviour
- keep all DLL loading outside the Python unittest process
- extend explicit fixture cleanup to include the native harness artifacts

The tests demonstrate that same-process preload silently controls backend
dependency binding. This provides the behavioural contract required for a
future fail-closed production preload and path-verification implementation.

No production behaviour changes and no GPU operation is performed.
Require COLI_HIP_RUNTIME_DIR when a Windows HIP host actually enters the
backend loader. Read and validate the setting with Unicode Windows APIs before
the existing backend-DLL load is attempted.

- distinguish a missing variable from an explicitly empty value
- reject relative and drive-relative paths
- require the configured path to exist as a directory
- require amdhip64_7.dll to exist beneath it as a file
- support spaces, Unicode values and trailing separators
- leave CUDA-mode loading completely unaffected
- cover the contract with CPU-only native-harness tests

This commit validates configuration only. It does not preload the runtime,
inspect loaded modules or enforce that the configured runtime supplies the
backend dependency. The existing configured-A and preloaded-B witness records
that unsafe behaviour for the subsequent binding-enforcement change.
Preserve and report the Windows errors from both existing backend-DLL load
attempts instead of describing every failure as a missing backend file.

- distinguish an absent candidate from an existing file that failed to load
- retain separate primary and fallback Windows error codes
- classify missing modules or dependencies, missing procedures and invalid PEs
- always include the numeric Windows loader error
- avoid unsupported claims about specific dependencies or ambient modules
- cover each diagnostic class with deterministic CPU-only DLL fixtures

The backend names, load order, search flags and fallback behaviour are
unchanged. This commit improves diagnostics only; it does not preload a HIP
runtime, inspect loaded modules or enforce runtime binding.
Add the Windows loader primitives needed to reason about a configured HIP
runtime before the production loader begins enforcing that decision.

- grow Unicode module and final-path buffers without a MAX_PATH bound
- compare physical files through symmetric modern or legacy identities
- enumerate every loaded amdhip64_7.dll with Toolhelp32
- distinguish empty, incomplete, single and multiple runtime inventories
- reduce verified inventory state to an explicit five-way binding decision
- cover hardlink aliases, repeated references and simultaneous runtimes
- expose value-only helper wrappers only in dedicated native test builds

The helpers are not called from coli_cuda_load and do not preload, accept or
reject a runtime. Runtime enforcement, Unicode HIP backend loading, fallback
removal and post-load verification remain for W1-B2d2.
Require a Windows HIP host to prove that the configured amdhip64_7.dll is the
only matching runtime before the backend may become available.

- inventory every loaded same-name runtime and compare physical file identity
- fail closed on wrong, multiple or unverifiable runtime state
- acquire one exact-path runtime reference and verify it after mapping
- load coli_hip.dll only from the absolute Unicode executable directory
- verify the runtime again after the backend maps and before symbol resolution
- release owned runtime and backend references on every failed initialization
- preserve the existing CUDA loader and document the experimental HIP setup

The post-load identity check detects path replacement after mapping but does
not make path-based loading atomic. Real gfx1151 runtime and model validation
remain for W1-B3.
Record how to build the synthetic glm_tiny oracle model without touching the
system Python or the repository.

- mark the tools requirements as development-only, not a Colibri runtime need
- name the minimal direct set for make_glm_oracle.py: torch, transformers, safetensors
- explain why transformers>=5.11 is a hard floor rather than a preference
- add an isolated CPU-only environment recipe and a verification command
- document that the generator has no --outdir and writes relative to its
  working directory, so it must run outside the repository
- show the teacher-forcing self-test against externally generated files

The model itself is not generated here, and no token has been verified.
The Windows HIP DLL path now builds AND loads: the host binds the runtime named
by COLI_HIP_RUNTIME_DIR and fails closed on an identity or duplicate-runtime
mismatch, so "build support only" and the deferred-runtime-selection note are
both stale.

Replace the Limitations list with what was actually tested and what it does not
claim: one configuration (Radeon 8060S reporting gfx1151, TheRock HIP 7.14.60850,
MSVC 14.44.35207, Windows SDK 10.0.26100.0); dense tensors on HIP with routed
experts still on CPU, which is not full-GPU MoE; the device line proves nothing
without "resident set: N tensors"; per-tensor CPU fallback keeps the exit code 0;
and normal model exits still rely on process teardown, as they already do on the
existing host path.

Still no hosted CI for the Windows HIP build or runtime - no runner provides the
toolchain or a GPU. Detailed setup stays in docs/windows.md.
…residency check

The AMD section still said no end-to-end GPU inference had been validated. It has
been, on one configuration, so state that precisely and fence off everything it
does not cover.

Add the three things that actually block a first successful build:

- pin VCToolsVersion to 14.44.35207. hipcc's clang picks the newest installed
  MSVC toolset, and 14.51 (Visual Studio 2026) has a <cmath> that collides with
  HIP __device__ overloads.
- override a machine-wide HIP_PATH inside the build shell only, and clear the
  ROCm toolchain variables, so a second install cannot redirect headers or
  device bitcode.
- System32 on PATH and a writable TMP/TEMP/TMPDIR, both of which MSYS2 shells
  commonly leave unset.

Add how to put tensors on the GPU and how to check that it happened: COLI_CUDA=1
alone places nothing, CUDA_DENSE=1 makes the dense tensors eligible, routed
experts stay on CPU, and "resident set: N tensors" - not the device line - is the
evidence. Document that per-tensor upload failure falls back to CPU with exit 0,
and that normal model exits rely on process teardown rather than an explicit
shutdown call, as the existing host path already does.

Replace the two author-local absolute paths in the oracle-tooling walkthrough
with a user-chosen directory and a $repo variable.
c/tests/test_backend_loader.py had no CI anywhere. The python job is Linux-only,
where the Windows loader contract does not apply, so 78 tests covering DLL-name
selection, runtime-directory validation, physical runtime identity and the
fail-closed binding decision never ran.

They need no GPU and no HIP SDK: the fixtures are stub DLLs built on the fly by
the MinGW gcc/objdump this job already installs. Add binutils for objdump and run
them on the existing windows-latest leg.

shell: pwsh overrides the job default deliberately. Under the MSYS2 shell the
UCRT64 interpreter reports os.sep == "/", so the loader's backslash-joined paths
compare unequal and the path tests fail; the same interpreter reports "\" from
PowerShell. The step asserts that before running anything.

Both MSYS2 bin directories go on PATH: ucrt64 for gcc/objdump and usr for make.
Without make the host-matrix class skips at setUpClass and the run silently loses
tests while still reporting OK, so the step also fails on any skip.

GPU and runtime selectors are cleared so nothing can reach a real device.
The two Windows path assertions compared the loader's output against
str(Path) directly. Both separator conventions can be live in one process:
under MSYS2 the temp root arrives as D:/a/_temp/msys64/tmp/... with forward
slashes while os.path.join adds a backslash, so the comparison fails on the
separator alone.

The loader is right - \ is the Windows separator and it should keep emitting
it - so normalise both sides in the test instead, the same way this file
already does in LoaderRuntimeBindingTest.

Drop the os.sep assertion from the Windows CI step with it. It encoded the
wrong diagnosis: os.sep can read \ while the temp root is still
forward-slashed, so the guard passed and the tests failed anyway. The
comparison no longer depends on either.
@Kenneth-Javier
Kenneth-Javier force-pushed the feature/windows-hip-native branch from e5ff643 to 1f76e49 Compare August 2, 2026 23:26
@Kenneth-Javier

Copy link
Copy Markdown
Author

Thanks — and yes, the teacher-forced gfx1151 run was token-exact against the
CPU path at the token-ID level.

The retained authoritative sweep contained two CPU teacher-forcing processes
and two hybrid-HIP teacher-forcing processes. All four runs scored 32/32, and
the predicted token ID matched position-by-position at every one of the 32
checked positions in both runs per mode.

This was an exact comparison of the selected token decisions, not a
close or tolerance-based comparison. I am not claiming bit-identical logits.

Separately, two CPU and two hybrid-HIP free-decode processes produced the same
exact 20-token autoregressive sequence, with prefix equality checked at
positions 1, 2, 4, 8, 16 and 20.

You were also right about the Windows CI failure. My earlier os.sep
explanation was incomplete: the temp root and the loader-appended filename can
use different separator conventions inside the same process.

The fix is test-only. The loader continues to emit the correct Windows \
separator, while the two assertions now compare normalized paths instead of
raw strings. I also removed the os.sep CI guard because it encoded the wrong
diagnosis.

The branch is now rebased onto dev at 7fb1159, with head
1f76e498cc346a0c324a946711ea799ef953250a. The loader suite passes locally
with 78/78 and zero skips under both exercised environments; the new hosted CI
run remains the authoritative confirmation for the runner configuration.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cuda Backend CUDA/NVIDIA feature Nuova funzionalità needs-rebase Confligge, serve rebase dell'autore

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants