Skip to content

Commit a93b6c0

Browse files
committed
fix(path-c): device-aware TileLang target (CUDA on non-Metal) + robust dev-build root
Path C fused compile hardcoded target=metal and the dev-build resolver pinned TVM_LIBRARY_PATH to a stub-only dir (no libtilelang.so) on gb10, so the planner failed and training fell back to the Metal-only eager kernel (unavailable on CUDA). Add _path_c_default_target() (cuda when mx.metal unavailable, else metal) and use it for all fused compile defaults; require libtilelang.so + honor TILELANG_DEV_BUILD_ROOT/ TILELANG_ROOT + derive the real tilelang build when resolving the dev env. Verified on gb10: lowerer runs target=cuda (sm_121), fused block JIT-compiles. Apple stays metal (no regression). Runtime install still gated by the value_and_grad grad-tree bridge wiring.
1 parent 3a88375 commit a93b6c0

3 files changed

Lines changed: 64 additions & 13 deletions

File tree

cppmega_mlx/runtime/path_c_fusion.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,19 @@
1919

2020
import mlx.core as mx
2121

22+
23+
def _path_c_default_target() -> str:
24+
"""Default TileLang lowering target for Path C kernels.
25+
26+
Metal on Apple silicon; CUDA elsewhere (e.g. gb10). ``mx.metal`` may exist
27+
as a module yet report ``is_available() == False`` on non-Metal hosts.
28+
"""
29+
metal = getattr(mx, "metal", None)
30+
if metal is None or not metal.is_available():
31+
return "cuda"
32+
return "metal"
33+
34+
2235
__all__ = [
2336
"BenchmarkAcceptanceRow",
2437
"CompiledPathCRegion",
@@ -2199,7 +2212,7 @@ def compile_mamba3_fp8_train_tilelang_region_from_prim_funcs(
21992212
fp8_prepare_spec: SparseMLAFp8PrepareSpec | None = None,
22002213
sparse_mla_fp8_prepared: Any,
22012214
schedule_template: Callable[[Any], Any] | None = None,
2202-
target: str = "metal",
2215+
target: str = _path_c_default_target(),
22032216
lowerer: Callable[..., Any] | None = None,
22042217
require_single_kernel: bool = True,
22052218
) -> Any:
@@ -2467,7 +2480,7 @@ def compile_path_c_region(
24672480
schedule_status: str = "ready",
24682481
tilelang_lowerer: Callable[..., Any] | None = None,
24692482
tilelang_plan_factory: Callable[..., Any] | None = None,
2470-
target: str = "metal",
2483+
target: str = _path_c_default_target(),
24712484
compiler: Callable[[FusionCompilePlan], object] | None = None,
24722485
) -> FusionCompilePlan | CompiledPathCRegion:
24732486
"""Create a TileLang/TVM region compile plan, optionally invoking compiler."""
@@ -2575,7 +2588,7 @@ def compile_path_c_region(
25752588
def tilelang_single_entry_lowerer(
25762589
func_or_mod: Any,
25772590
*,
2578-
target: str = "metal",
2591+
target: str = _path_c_default_target(),
25792592
execution_backend: str = "tvm_ffi",
25802593
compile_prim_func: Callable[..., Any] | None = None,
25812594
**_kwargs: Any,

cppmega_mlx/runtime/path_c_fusion_schedules.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
from cppmega_mlx.runtime.path_c_fusion import (
2424
CompiledPathCRegion,
25+
_path_c_default_target,
2526
FusionCompilePlan,
2627
FusionKernelSurface,
2728
FusionScheduleContractStatus,
@@ -13499,7 +13500,7 @@ def compile(
1349913500
self,
1350013501
*,
1350113502
tilelang_lowerer: Callable[..., Any],
13502-
target_name: str = "metal",
13503+
target_name: str = _path_c_default_target(),
1350313504
) -> CompiledPathCRegion:
1350413505
"""Compile the selected target through its descriptor schedule template."""
1350513506

@@ -14467,7 +14468,7 @@ def plan_mamba3_fp8_train_fusion_schedule(
1446714468
def compile_mamba3_fp8_train_fusion_schedule(
1446814469
*,
1446914470
tilelang_lowerer: Callable[..., Any] | None = None,
14470-
target_name: str = "metal",
14471+
target_name: str = _path_c_default_target(),
1447114472
include_backward: bool = True,
1447214473
model_config: Any | None = None,
1447314474
max_rows_per_launch: int | None = DESCRIPTOR_DEFAULT_MAX_ROWS_PER_LAUNCH,

scripts/m04_train_step.py

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1058,11 +1058,36 @@ def fp8_path_c_producer_gate_payload(
10581058
}
10591059

10601060

1061+
def _path_c_default_target() -> str:
1062+
"""Resolve the default lowering target for Path C fused/direct-chain kernels.
1063+
1064+
On Apple silicon we lower to Metal; everywhere else (e.g. CUDA gb10) Metal is
1065+
unavailable, so fall back to CUDA. Detection mirrors mlx: ``mx.metal`` may be
1066+
present as a module yet report ``is_available() == False`` on non-Metal hosts.
1067+
"""
1068+
metal = getattr(mx, "metal", None)
1069+
if metal is None or not metal.is_available():
1070+
return "cuda"
1071+
return "metal"
1072+
1073+
10611074
def _tilelang_dev_roots() -> tuple[Path, ...]:
10621075
roots: list[Path] = []
1063-
env_root = os.environ.get("TILELANG_DEV_BUILD_ROOT")
1064-
if env_root:
1065-
roots.append(Path(env_root).expanduser())
1076+
for env_name in ("TILELANG_DEV_BUILD_ROOT", "TILELANG_ROOT"):
1077+
env_root = os.environ.get(env_name)
1078+
if env_root:
1079+
roots.append(Path(env_root).expanduser())
1080+
# Derive the build dir of the installed `tilelang` package (real build with
1081+
# libtilelang.so), e.g. /home/dave/source/tilelang/build, as a candidate.
1082+
try:
1083+
import importlib.util
1084+
1085+
spec = importlib.util.find_spec("tilelang")
1086+
if spec is not None and spec.origin:
1087+
pkg_build = Path(spec.origin).parent.parent / "build"
1088+
roots.append(pkg_build)
1089+
except Exception:
1090+
pass
10661091
roots.extend(
10671092
[
10681093
ROOT.parent / "tl_apache_tvm_swap",
@@ -1110,6 +1135,10 @@ def ensure_tilelang_dev_env_for_path_c() -> None:
11101135
tvm_dir = build_root / "tvm"
11111136
if not lib_dir.exists() or not tvm_dir.exists():
11121137
continue
1138+
# Skip stub-only build dirs (e.g. tl_apache_tvm_swap/build holds only
1139+
# libcudart_stub.so etc.). A usable dev build must ship libtilelang.so.
1140+
if not (lib_dir / "libtilelang.so").exists():
1141+
continue
11131142
os.environ["TILELANG_DEV_BUILD_ROOT"] = str(build_root)
11141143
os.environ.setdefault("TVM_LIBRARY_PATH", str(lib_dir))
11151144
_prepend_env_path("DYLD_LIBRARY_PATH", lib_dir)
@@ -3225,7 +3254,7 @@ def fp8_path_c_training_route_payload_for_model(
32253254
compile_receipt_path: str | Path | None = None,
32263255
auto_install_fused_train_block: bool = False,
32273256
fused_train_block_artifact_lowerer: Callable[..., Any] | None = None,
3228-
fused_train_block_artifact_target_name: str = "metal",
3257+
fused_train_block_artifact_target_name: str = _path_c_default_target(),
32293258
fused_train_block_artifact_execution_backend: str = "tvm_ffi",
32303259
) -> dict[str, Any]:
32313260
"""Return Path C training route metadata using model-owned ABI banks."""
@@ -4759,7 +4788,7 @@ def compile_path_c_fused_train_block_artifact_for_model(
47594788
*,
47604789
model: Any,
47614790
sequence_length: int | None = None,
4762-
target_name: str = "metal",
4791+
target_name: str = _path_c_default_target(),
47634792
execution_backend: str = "tvm_ffi",
47644793
lowerer: Callable[..., Any] | None = None,
47654794
) -> dict[str, Any]:
@@ -5556,7 +5585,7 @@ def install_path_c_fused_train_block_runtime_for_model(
55565585
training_runtime: Any | None = None,
55575586
compile_artifact: bool = False,
55585587
artifact_lowerer: Callable[..., Any] | None = None,
5559-
artifact_target_name: str = "metal",
5588+
artifact_target_name: str = _path_c_default_target(),
55605589
artifact_execution_backend: str = "tvm_ffi",
55615590
sequence_length: int | None = None,
55625591
) -> dict[str, Any]:
@@ -6020,7 +6049,7 @@ def _path_c_direct_chain_artifact_for_segment(
60206049
def compile_path_c_direct_fusion_chain_artifacts(
60216050
chain: Any,
60226051
*,
6023-
target_name: str = "metal",
6052+
target_name: str = _path_c_default_target(),
60246053
execution_backend: str = "tvm_ffi",
60256054
lowerer: Callable[..., Any] | None = None,
60266055
) -> tuple[Any, ...]:
@@ -6661,7 +6690,8 @@ def install_path_c_direct_chain_training_runtime_for_model(
66616690
chain: Any | None = None,
66626691
artifacts: Any | None = None,
66636692
logical_owner: Any | None = None,
6664-
artifact_compiler: Callable[[Any], Any] = compile_path_c_direct_fusion_chain_artifacts,
6693+
target_name: str | None = None,
6694+
artifact_compiler: Callable[..., Any] | None = None,
66656695
owner_name: str | None = None,
66666696
sequence_length: int | None = None,
66676697
training_critical_path: bool = False,
@@ -6733,6 +6763,13 @@ def install_path_c_direct_chain_training_runtime_for_model(
67336763
"training_critical_path": False,
67346764
}
67356765

6766+
resolved_target_name = target_name or _path_c_default_target()
6767+
if artifact_compiler is None:
6768+
def artifact_compiler(_chain: Any) -> Any:
6769+
return compile_path_c_direct_fusion_chain_artifacts(
6770+
_chain,
6771+
target_name=resolved_target_name,
6772+
)
67366773
compiled_artifacts = artifacts if artifacts is not None else artifact_compiler(selected_chain)
67376774
resolved_owner_name = owner_name or (
67386775
f"{profile_name}.path_c_direct_fusion_chain_training_runtime"

0 commit comments

Comments
 (0)