Repository navigation
[AI][BUG][Kunlunxin] libxpytorch.so cannot bind torch 2.10: c10_cuda_check_implementation ABI signature break (…ib vs …jb), first flagos op hangs forever #573
Description
Issue Type
- Bug Report
AI Agent Information
- Agent: Claude Code CLI
- Model: Claude (1M context)
- Session Context: Working the goal "fix all bert-related issues so every chip fully passes bert, including nvidia". 8 of 10 fleet chips are now measured at 151/0 on a compliant stack. Kunlunxin and TsingMicro are the remaining two. This issue files the Kunlunxin half — a vendor plugin / torch ABI break — with exact symbols from both sides of the boundary.
Summary
Kunlunxin's XPU plugin libxpytorch.so cannot bind to this torch build: it imports _ZN3c104cuda29c10_cuda_check_implementationEiPKcS2_ib, while the installed torch/lib/libc10_cuda.so defines only _ZN3c104cuda29c10_cuda_check_implementationEiPKcS2_jb. The differing final parameter (ib = unsigned int, bool vs jb) is a torch 2.9 → 2.10 C++ signature change, so this is an ABI break inside libc10_cuda.so rather than a missing symbol — which is exactly why the dynamic loader emits only a warning instead of failing hard. The plugin then does not bind, and the first flagos operation spins forever (100% CPU, never returns). No route change can pass bert on this chip until the plugin and torch agree.
Environment
Click to expand environment details
- Platform: Kunlunxin XPU (P800-96G)
- Host:
bm-dhrj-bj-zone8-d-p800-96g-1-13, kernel5.4.119-19-0009.56 - Container:
lvyufeng-torch-fl - Python: 3.10 (env
torch-fl-hf-test) - PyTorch: 2.10.0+cu129 (vendor)
- torch_fl:
c2ded4b(upstream main), editable atkx-c2ded4b - torch_xmlir plugin:
<env>/lib/python3.10/site-packages/torch_xmlir/libxpytorch.so - 8 devices visible once the env is conda-activated
Runtime config:
# Must conda-activate: activate.d sets a vendor-first LD_LIBRARY_PATH
docker exec lvyufeng-torch-fl bash -lc \
'source /public-flash/lvyufeng/miniconda3/etc/profile.d/conda.sh; \
conda activate torch-fl-hf-test; python ...'Reproduction
E=/public-flash/lvyufeng/miniconda3/envs/torch-fl-hf-test
# 1. What libc10_cuda.so DEFINES
nm -D --defined-only $E/lib/python3.10/site-packages/torch/lib/libc10_cuda.so \
| grep c10_cuda_check_implementation
# 2. What the vendor plugin WANTS
nm -D --undefined-only $E/lib/python3.10/site-packages/torch_xmlir/libxpytorch.so \
| grep c10_cuda_check_implementation
# 3. The symptom: a plain flagos op never returns
docker exec lvyufeng-torch-fl bash -lc \
'source /public-flash/lvyufeng/miniconda3/etc/profile.d/conda.sh; \
conda activate torch-fl-hf-test; \
timeout 60 python -c "import torch, torch_fl; \
print(torch.cuda.is_available(), torch.cuda.device_count()); \
x = torch.randn(4, 4, device=\"flagos\"); print(x.sum().item())"'Expected vs Actual Behavior
Expected: torch.cuda.is_available() → True, 8 devices, and the flagos op returns.
Actual — the ABI break, exact symbols from both sides:
$ nm -D --defined-only .../torch/lib/libc10_cuda.so | grep c10_cuda_check_implementation
00000000000440c0 T _ZN3c104cuda29c10_cuda_check_implementationEiPKcS2_jb
$ nm -D --undefined-only .../torch_xmlir/libxpytorch.so | grep c10_cuda_check_implementation
U _ZN3c104cuda29c10_cuda_check_implementationEiPKcS2_ib
Actual — the runtime symptom:
WARNING: import hook error: .../torch_xmlir/libxpytorch.so: undefined symbol:
_ZN3c104cuda29c10_cuda_check_implementationEiPKcS2_ib
torch.cuda.is_available() is True and device_count is 8 — the plugin's import emitted only a warning — but the first flagos op spins at 100% CPU and timeout kills it (exit 124). No bert count is reachable.
Root Cause Analysis
Mechanism: a C++ signature change in c10 across torch minor versions.
Demangling the two names:
- Definition (
jb):c10::cuda::c10_cuda_check_implementation(int, char const*, char const*, unsigned int, bool) - Reference (
ib):c10::cuda::c10_cuda_check_implementation(int, char const*, char const*, int, bool)
torch 2.9 exported the …ib form (4th parameter int); this build is torch 2.10, which widened that parameter to unsigned int and therefore exports …jb. libxpytorch.so was built against the 2.9 form. Because C++ mangled names encode the full signature, the two are different symbols, so libxpytorch.so's reference is unresolved.
Why it only warns instead of failing to load: the symbol name differs, not the library. dlopen of libxpytorch.so does not fail outright in this configuration — the import hook reports the unresolved symbol as a warning — so torch and torch_fl import successfully and the failure is deferred. The deferred failure is a hang, not an exception, because the vendor runtime spins on a path that never completes. This is why the chip reads as "imports fine, then hangs" and why the symptom is easy to misattribute to a torch_fl routing defect.
Why it is not route-dependent (important for triage): the spin is op-shape-dependent, not xdnn/vendor-route-dependent. x@y via the vendor route passes, and a flagos→cpu copy passes, while both x*y via the vendor route and a non-vendor aten.add hang. So it is not "the vendor op path is broken"; it is that the plugin's device management is unusable once it fails to bind.
Why this is not a torch_fl defect: the unresolved symbol is inside torch/lib/libc10_cuda.so (torch's own library) and the reference is inside a vendor .so (libxpytorch.so). torch_fl's route configuration operates above both. No conf/codegen change moves either symbol.
Proposed Solution
Two viable directions, both infrastructure/vendor-side:
- Align the plugin with torch 2.10 — rebuild
libxpytorch.soagainst this torch build so it references the…jbform. This is the forward-looking fix. - Pin the plugin to its matching torch minor — install the torch 2.9-era build the plugin was compiled against, and pin it, so the
…ibreference resolves.
A partial workaround worth stating explicitly because it is not sufficient: shipping the ABI mismatch as a hard error instead of a warning would convert the hang into a legible failure, but it would not make bert pass. It is a diagnosability improvement, at best a follow-up.
No change to torch_fl is proposed or needed.
Verification Plan
-
nm -D --defined-only .../torch/lib/libc10_cuda.so | grep c10_cuda_check_implementationand the same forlibxpytorch.sonow show the same mangled name - The
WARNING: import hook error ... undefined symbolline is absent at import -
torch.randn(4, 4, device="flagos")(andtorch.ones(4, 4, device="flagos") * 2) returns instead of timing out - The non-vendor
aten.addcase that currently hangs returns - Suite-level: run the bert suite in
lvyufeng-torch-fl, envtorch-fl-hf-test, treec2ded4b, and report the= N passed / M failed =line
Context & Investigation
- Started from "import torch / import torch_fl fail", which turned out to be a measurement artifact: calling
$E/bin/pythonwithoutconda activateskipsactivate.d, which sets a vendor-firstLD_LIBRARY_PATH(/usr/local/xpu/lib:/usr/local/xcudart/lib:<torch_xmlir>:$CONDA_PREFIX/lib). Without it the XPU import hook falls back to re-executingtorch/__init__.pyandtorch/library.pyraisesRuntimeError: Only a single TORCH_LIBRARY can be used to register the namespace triton. That double-triton error is a missing-LD_LIBRARY_PATHartifact, not a torch defect — withconda activateboth imports succeed. This issue is about what remains after that. - Checked whether the plugin loads at all: it does — ops reach the vendor runtime — so this is not a "plugin missing" problem.
- Tested whether the hang is route-specific: it is not.
x@y(vendor) and aflagos→cpu copy both pass;x*y(vendor) and a non-vendoraten.addboth hang. Shape-dependent, not route-dependent. That refuted the earlier working theory that thexdnn/vendor route was the culprit. - Extracted the unresolved symbol name from the import hook warning and compared it against the definition in torch's own
libc10_cuda.sowithnmon both files — this is what converted a vague "unresolved symbol" into a pinned ABI signature break. - Ruled out the py311 env as a red herring:
torch-fl-xpu-py311imports cleanly but carries no flagtree/triton and reportsdevs 0, so its apparent health is not evidence.
Superseded reading (retracted): an earlier note attributed the residual to "index.Tensor needs a dispatch key" / a normal_ route defect. That is not supported: the trigger point varies with op shape and the ABI break above explains the hang. Do not re-file the index/dispatch theory without new evidence.
Related Code Locations
No torch_fl source location is implicated. The failing boundary is:
torch/lib/libc10_cuda.so— defines_ZN3c104cuda29c10_cuda_check_implementationEiPKcS2_jbtorch_xmlir/libxpytorch.so— references_ZN3c104cuda29c10_cuda_check_implementationEiPKcS2_ib- XPU import hook (
xpytorch_import_hook→torch_xray_import_hook) — reports the unresolved symbol as a warning
Related Issues
- [AI] [BUG][Kunlunxin] Two-device scatter/gather returns uninitialized rows: the cross-card D2D copy never reaches its destination #544, [AI][BUG][Kunlunxin] aten::empty.memory_format is not implemented for 'CUDA' on the flagos device #510, [AI][BUG][Kunlunxin] cat hands a CPU tensor to xdnn as device memory (no device gate) #523 — Kunlunxin bugs previously filed as code defects, none of which can be validated while the device is unusable.
- fix(ascend): route flip to its aclnn kernel off the FlagGems override #567 reported the same class of problem from an independent angle.
Checklist - AI Agents MUST Complete All
- I have provided complete environment information
- I have included a minimal, self-contained reproducer
- I have included full error output with traceback
- I have analyzed the root cause (not just symptoms)
- I have proposed a specific solution with implementation approach
- I have identified affected code locations with line numbers
- I have described how to verify the fix
- I have checked for duplicate issues
- All text is in English (required per CLAUDE.md)
- Code follows project conventions (checked existing code style)
Activity
lvyufeng commentedon Oct 17, 2026
Correction: this is a fleet-environment provisioning fault, not a chip-level ABI break
Investigated further on 2026-10-17. The ABI mismatch in the original report is real, but its
framing is misleading: kunlunxin's own CI lane passes, on a different stack than the one
this issue was measured on.
Evidence 1 — the kunlunxin CI lane is green
On run
38856759463, job116830306874(Platform pipeline (kunlunxin) / Build and test (Kunlun P800 XPU)):That step is not a no-op.
.github/integration-manifest.ymldefines the kunlunxinvendor_opsgroup as:Evidence 2 — CI runs a DIFFERENT stack than the host this issue was measured on
.github/configs/kunlunxin.yml+.github/scripts/set_env_kunlunxin.shstate the contract:ci_imageflagos-runtime-kunlunxin-xre5.37.1:2.2.0environment_check.torch_version_base2.10.0environment_check.cuda_versionnullenvironment_check.flagos_devicesat_least: 1environment_check.triton_backendxpu(flagtree, not stock triton)The hook is explicit: kunlunxin is a native PrivateUse1 backend — the port's runtime
under
csrc/runtime/accelerator/kunlunxin/calls the vendor libxpurt runtime, kernelscome from the vendor xdnn library, "There is no CUDA boxing", and "a stock CPU
PyTorch wheel is enough to build and run the backend."
Evidence 3 — the host violates that contract in every env
Host
bm-dhrj-bj-zone8-d-p800-96g-1-13, containerlvyufeng-torch-fl, image...kunlunxin-xre5.37.1:**2.1.2**(not CI's 2.2.0):Every candidate pairs a
+cu129vendor torch with atorch_xmlirplugin built againsta different torch ABI. No env satisfies
cuda_version: null. The…ibvs…jbsymbol inthe original report is one instance of that same mismatch; the
/tmp/xpu210vvenv shows asecond, different one (
triu_).What the symbol mismatch actually is
_ZN3c104cuda29c10_cuda_check_implementationEiPKcS2_ib(referenced) vs…jb(defined) is atorch 2.9 → 2.10 signature widening — between two vendor components (a CUDA torch and a
torch_xmlirplugin), inside a stack that kunlunxin's CI configuration says must not containa CUDA torch at all.
Corrected statement
no code change can help."
CPU-torch stack. The fleet host is provisioned with a vendor-CUDA stack whose
torch_xmlirplugin has an ABI break against its torch. That is an environment/provisioning problem.
What remains genuinely unknown
CI runs
tests/integration/ops/, not the HF bert suite. Whether kunlunxin's bert suitepasses on a correctly-provisioned fleet env is not yet measured — that gap is real.
Suggested next step
Provision the fleet host to the CI contract (the
:2.2.0image, or a stock CPU-torch venvwith flagtree-xpu and no
torch_xmlir), then run the bert suite. If it passes, this issueshould be reclassified as a provisioning bug in the fleet host, not a kunlunxin port defect.
I am not changing any code for this; the port itself is not implicated by any of the above.
lvyufeng commentedon Oct 17, 2026
Closing the gap the correction left open: bert is still not measured, and the blocker is now located in the vendor runtime
The correction above ends at the right question — "Whether kunlunxin's bert suite
passes on a correctly-provisioned fleet env is not yet measured" — and proposes
provisioning to the CI contract, then running bert. That provisioning was done. This
comment reports what it produced, and it refines the attribution rather than
overturning the correction.
The env gate was cleared, and the port does run
Built exactly as the correction prescribes — a venv with
torch==2.10.0+cpu(
torch.version.cuda is None) and notorch_xmlir, then a wheel built withFLAGOS_ACCELERATOR=kunlunxin XDNN_ROOT=… FLAGOS_BUILD_FLAGGEMS_CPP=0against thatCPU torch. Linking against CPU torch is what keeps
libtorch_cuda.soout of thewheel's
NEEDEDlist; the oracle agrees:So the ABI mismatch described in the original report is gone on this stack, as the
correction predicted. And the port works: the
vendor_opsgroup from.github/integration-manifest.ymlpasses —But bert still cannot be measured, for a second and different reason
The ops group is narrow. It exercises
mm/bmm/randn, which the conf routesto
kunlunxinand which survive. The bert forward opens withnn.Embedding, and thathangs. Reproduced on the same wheel, same md5, same box:
Never reaches
FORWARD_OK. No pytest summary line was ever produced by any run — everyattempt was terminated by the hang, never by pytest. bert is NOT MEASURED on
kunlunxin, and that is the honest ceiling here.
There is no de-routed control arm — the conf and the registration are in lockstep
The obvious discriminator ("does bert finish if the vendor route is bypassed?") cannot
be constructed on this wheel, and that is itself a finding:
nonein the conf raises, rather than falling back:embedding: routed to 'none' … but the op is registered on PrivateUse1 -- regenerate the vendor conf so registration and routing agree(dispatcher.h:306-312; thekNonepathis only reachable when codegen skipped
m.impl()).dispatcher.h:281-304) all bottom out atcuda_fn_/flaggems_fn_/flaggems_cpp_fn_; on this wheelcuda_fn_is empty (no boxing kernel) andflag_gemsis not importable at all, so they cannot yield
cpu_fallbackeither.csrc/aten/register.cc:556-566(theUSE_KUNLUNXINarm) —kunlunxin_register.incclaims exactly the ops the conf routes tokunlunxin. Counted onthe wheel-owning tree: 58
m.implclaims vs 57= kunlunxinroutes, withembeddingand
sum.dim_IntListamong the claims. Conf and registration are deliberately in lockstep,so every value that de-routes a claimed op is a disagreement → raise. Reaching
cpu_fallbackwould require rebuilding with a reduced.inc— a different md5,outside this measurement.
FLAGOS_FORCE_BACKENDoffers no lever:_env.py:413-420documents onlyflaggems/vendor/tileops. (An earlier probe of mine claimed=flaggemsreproducedthe spin; that arm was invalid — the dispatch log shows
FLAGOS_FORCE_BACKEND=flaggems: 0 ops repinned, 2037 left as configuredandembedding -> kunlunxin. FlagGems implements none of these ops here, so nothing moved.Retracted.)
Therefore the A/B separation is NOT established, and this comment does not claim it.
Where the hang actually is
Per-thread
/proc/<pid>/tasksampling of a live spinner, twice, 3s apart:A thread blocked on a mutex someone else holds looks like those 65 — futex wait, ~0% CPU.
The spinner does the opposite: a full core in the low-level lock fast path
(
lll_mutex_unlock_optimized, frame #0) with no other thread holding or progressing thatlock. So this is a single-thread userspace self-loop in the vendor stack, not a futex
deadlock and not thread starvation — no device or neighbour action can clear it.
Stack, verified against the shipped binaries:
Two corrections to the shape of that stack, both material:
DT_NEEDEDis onlylibrt/libpthread/libdl/libstdc++/libm/libgcc/libc; libxpucuda arrives via adlopenstring (
?libxpucuda.so.1). So the edge is a runtime-dlopened call, not a static one..symtab) — which is exactly why Uni wrapper #3-[Bug] torch_fl and torch_npu conflict: duplicate PrivateUse1 backend fallback registration on Ascend platform #11 render as??. They are resolved-but-stripped, not unknown code. It is a CUDA-compat shim, exportingcuCtxSynchronize,cuStreamSynchronize,cuStreamWaitEvent,cuStreamWaitValue32/64;xpu_waitforwards into acuStreamSynchronizereimplementation and the lock lives there.kunlunxin_common.h:117-118confirms the sync isxpu_wait(nullptr)draining the stream.Device health at the same moment: no hw exception, ECC 0, temps 36-44C. (An earlier
"XPU 0 at 100% util" reading was a live sibling spinner's stream, not a hardware fault.)
Corrected statement
pass". Neither is what the evidence shows.
CPU-torch build; and bert is blocked by a vendor-runtime defect — a self-loop inside
libxpucuda.so.1's stream-synchronisation lock, reached fromlibxpurt'sxpu_wait, on anop that is properly registered and routed. Resolving it needs a vendor SDK fix, not a
torch_fl change.
remaining measurement, and it is gated on the vendor, not on this repo.
Closing this comment's scope as a vendor-runtime blocker. If the vendor
xpucudaxpu_waitsync is fixed, reruntests/models/bert/test_modeling_bert.pyon the sameCPU-torch stack above and this issue can be revisited with a real tally.