neuregex opened a new issue, #20352:
URL: https://github.com/apache/tvm/issues/20352
### Expected behavior
`tests/python/codegen/test_target_codegen_hexagon.py` compiles three small
modules for
`qcom/hexagon-v66` and inspects the generated assembly and LLVM IR. It
registers a phony linker
(`/bin/true`) with the comment *"so that we can test codegen without a
Hexagon toolchain"*, so I
expected it to pass on any build whose LLVM has the Hexagon target — no SDK,
no device.
### Actual behavior
Those three tests never run, and they cannot pass on main.
They are gated on `env.has_hexagon()`, which requires both
`HEXAGON_TOOLCHAIN` and
`ANDROID_SERIAL_NUMBER` (an attached device), so they are always skipped —
there is no `hexagon`
mention left under `ci/` or `.github/`. The Hexagon codegen is still
compiled into every
LLVM-enabled build though: `src/backend/hexagon/codegen/llvm/*.cc` is in
`COMPILER_LLVM_SRCS`
next to the CUDA and ROCm LLVM codegen (`cmake/modules/LLVM.cmake:62`),
independent of
`USE_HEXAGON`, which only controls the device runtime. So it gets recompiled
and carried through
refactors on every build, with no test exercising it.
When I narrow the gate so the tests run on a plain `USE_LLVM` build
(`USE_HEXAGON=OFF`, no
toolchain, no device), all three fail, for two independent reasons.
**1. `unknown intrinsic tirx.tvm_call_packed`**
```
src/target/llvm/codegen_llvm.cc:1545: in CodeGenLLVM::CreateIntrinsic
E tvm.error.InternalError: unknown intrinsic ir.Op(...
name="tirx.tvm_call_packed" ...)
```
`split_host_device_mods` decides host-ness by target kind
(`python/tvm/tirx/build.py:104`):
```python
def is_host_func(f):
target = f.attrs.get("target", tvm.target.Target("llvm"))
return target.kind.name in ["llvm", "c"]
```
Its own docstring says something else: *"Functions with `cpu` in the target
string are considered
host functions"*. The `hexagon` kind registers `set_default_keys({"hexagon",
"cpu"})`
(`src/backend/hexagon/codegen/target_kind.cc:42`), so with `Target(target,
target)` — target and
host both hexagon, which is what the test does — the host function is
classified as a device
function. Device modules only get `finalize_device_passes()`, which does not
include
`LowerTVMBuiltin()`. So the `tvm_call_packed` that `MakePackedAPI` inserts
for the `set_device`
call (`src/tirx/transform/make_packed_api.cc:261`) is never lowered to
`tvm_call_packed_lowered`,
the form `CodeGenCPU` knows how to handle.
Only `llvm`, `c` and `hexagon` register the `cpu` key, so this gap affects
exactly one target kind.
**2. `Cannot open /tmp/tvm-XXXXXX.so`**
With (1) worked around locally, the failure moves to:
```
<unknown>:0: in tvm::codegen::BuildHexagon(tvm::IRModule, tvm::Target)
src/runtime/file_utils.cc:83: in LoadBinaryFromFile
E tvm.error.InternalError: Check failed: (!fs.fail()) is false: Cannot
open /tmp/tvm-52eea3.so
```
`BuildHexagon` emits asm, obj, IR and bitcode into strings, then calls
`tvm.contrib.hexagon.link_shared` and reads the linked `.so` back from disk:
```cpp
std::string so_data;
runtime::LoadBinaryFromFile(so_name, &so_data);
return
target::HexagonModuleCreateWithFallback(ffi::Bytes(std::move(so_data)), ...);
```
Before #19757 (2026-06-13) that tail was `return
HexagonModuleCreate(so_name, "so", ...)`: the
filename was passed along and never opened, which is what made the
phony-linker approach work.
With the read-back, codegen-only use needs a real Hexagon linker, even
though every artifact the
test inspects has already been produced by that point. #19796 (2026-06-16)
removed the Hexagon app
and test wrappers three days later, so nothing was left that could have
caught it.
### Environment
- main at `87b5b27dac`, also reproduced at `cc0f9f07c1`
- Ubuntu 24.04 (WSL2), LLVM 18.1.3 via `llvm-config-18 --ignore-libllvm
--link-static`,
CMake 3.28.3, Python 3.12
- `USE_HEXAGON=OFF`, no Hexagon SDK, no device, `HEXAGON_TOOLCHAIN` and
`ANDROID_SERIAL_NUMBER`
unset
### Steps to reproduce
Build with `USE_LLVM` pointing at any LLVM that lists Hexagon in
`llvm-config --targets-built`
(the stock Ubuntu `llvm-18-dev` does), then:
```bash
unset HEXAGON_TOOLCHAIN ANDROID_SERIAL_NUMBER
pytest tests/python/codegen/test_target_codegen_hexagon.py -v
# 3 skipped: need hexagon
```
Add a narrower probe in `python/tvm/testing/env.py` and gate the three tests
with it instead of
`has_hexagon()`:
```python
@functools.cache
def has_hexagon_codegen() -> bool:
if not has_llvm():
return False
try:
return any(str(t).lower() == "hexagon" for t in
tvm.target.codegen.llvm_get_targets())
except Exception: # pylint: disable=broad-except
return False
```
```
# 3 failed: unknown intrinsic tirx.tvm_call_packed
```
Then, for the second failure, widen `is_host_func` to honour the `cpu` key:
```python
return target.kind.name in ["llvm", "c"] or "cpu" in target.keys
```
```
# 3 failed: Cannot open /tmp/tvm-XXXXXX.so
```
That one-liner is regression-clean here —
`tests/python/{codegen,tirx,driver,target,testing}` gives
1605 passed, 3195 skipped, nothing newly failing — but I don't think it is
the right fix: it would
also send a Hexagon *device kernel* to the host module in the usual
cross-compilation setup
(`target=hexagon`, `host=llvm`), and a split on `calling_conv` looks closer
to what the docstring
describes.
### Questions before sending a PR
Both fixes look like design calls rather than obvious patches:
1. Should `split_host_device_mods` key on `calling_conv` (or on the host
target) instead of the
target kind?
2. Should `BuildHexagon` keep codegen usable without a linker — loading the
`.so` only when one was
actually produced — or should the test stop going through `tvm.compile`
to inspect generated
code?
I'm happy to send a PR for whichever shape you prefer, together with the
test-gate change so this
is covered by CI from then on. My repro needs no device and no SDK, just an
LLVM with the Hexagon
target.
### Triage
* needs-triage
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]