amd: fallback to llvm when comgr is not available (#15914)

This commit is contained in:
2026-04-24 23:30:16 -04:00
committed by GitHub
parent 4b908b6e2c
commit 57fbaa3d49
11 changed files with 30 additions and 26 deletions
+1 -1
View File
@@ -153,7 +153,7 @@ class TestDevVar(unittest.TestCase):
self.assertEqual(DEV.target("CPU"), Target("CPU"))
def test_dev_arch_override(self):
with Context(DEV="NULL:HIP:gfx1100"):
with Context(DEV="NULL::gfx1100"):
self.assertEqual(Device["NULL"].renderer.target.arch, "gfx1100")
class MockCompiler(Compiler):
+1 -1
View File
@@ -722,7 +722,7 @@ class TestCfg(unittest.TestCase):
gidx = UOp.special(1, "gidx0")
sink = UOp.sink(out.base, lidx, gidx, arg=KernelInfo(name=name))
return UOp(Ops.PROGRAM, src=(sink, UOp(Ops.DEVICE, arg="NULL"), UOp(Ops.LINEAR, src=tuple([UOp(Ops.INS, arg=x) for x in insts]))))
with Context(DEV=f"NULL:HIP:{self.arch}"):
with Context(DEV=f"NULL::{self.arch}"):
out = Tensor.custom_kernel(Tensor.empty(1), fxn=fxn)[0]
prg = out.schedule()[-1].lower().prg.p
return amdgpu_cfg(prg.lib, self.arch)
+1 -1
View File
@@ -293,7 +293,7 @@ class Compiled:
f"{self.device}_{rn}=1 is deprecated, use DEV={self.device}:{rn} or {self.device}_CC={rn} instead"
t = DEV.target(self.device.split(':')[0], **({"arch":self.arch} if self.arch else {}))
return select_first_inited(select_by_name(self.renderers, self._renderer_name, t.renderer, f"{self.device} has no renderer {t.renderer!r}"),
f"No renderer for {self.device} is available", self.cached_renderer, target=t)
f"No renderer for {self.device} is available", self.cached_renderer, t)
def count(self) -> int:
"""
+4 -4
View File
@@ -133,13 +133,13 @@ def select_by_name(candidates:Sequence[T], get_name:Callable[...,str], query:str
raise RuntimeError(err_msg + (f", did you mean: {m[0]!r}?" if (m:=difflib.get_close_matches(query, map(get_name, candidates))) else ""))
return ret
def select_first_inited(candidates:Sequence[Callable[...,T]], err_msg:str, cache:dict|None=None, **kwargs):
def select_first_inited(candidates:Sequence[Callable[...,T]], err_msg:str, cache:dict|None=None, *args):
excs = []
for typ in candidates:
if cache is not None and typ in cache: return cache[typ]
if cache is not None and (typ,) + args in cache: return cache[(typ,) + args]
try:
x = typ(**kwargs)
if cache is not None: cache[typ] = x
x = typ(*args)
if cache is not None: cache[(typ,) + args] = x
return x
except Exception as e: excs.append(e)
raise excs[0] if len(excs) == 1 else ExceptionGroup(err_msg + " is available", excs)
+2 -2
View File
@@ -11,7 +11,7 @@ from tinygrad.runtime.autogen.amd.rdna3.ins import s_code_end # same encoding as
from tinygrad.runtime.autogen.amd.cdna.ins import s_nop as s_nop_cdna
_arch_map = {"gfx9": "cdna", "gfx10": "rdna3", "gfx11": "rdna3", "gfx12": "rdna4"}
def assemble_linear(ctx, prg:UOp, lin:UOp) -> bytes:
def assemble_linear(prg:UOp, lin:UOp, arch:str) -> bytes:
insts = [u.arg for u in lin.src]
# ** scan for max vgpr/sgpr/accvgpr
@@ -41,7 +41,7 @@ def assemble_linear(ctx, prg:UOp, lin:UOp) -> bytes:
elif u.op is Ops.DEFINE_LOCAL: lds_size += u.ptrdtype.size * u.ptrdtype.base.itemsize
elif u.op is Ops.SPECIAL and u.arg.startswith("gidx"): gids.add(int(u.arg[-1]))
code_bytes = b"".join(inst.to_bytes() for inst in insts)
arch = next(v for k, v in _arch_map.items() if ctx.target.arch.startswith(k))
arch = next(v for k, v in _arch_map.items() if arch.startswith(k))
is_cdna, is_rdna4 = arch == "cdna", arch == "rdna4"
# ** pad text to ISA alignment
+5 -8
View File
@@ -473,10 +473,10 @@ class HIPRenderer(CStyleLanguage):
def is_cdna(arch): return arch.split(":")[0] in {"gfx942", "gfx950"}
@staticmethod
def is_cdna4(arch): return arch.split(":")[0] == "gfx950"
def __init__(self, target:Target): # gfx942 => MI300, gfx1100 => RX 7900, gfx1201 => RX 9700
def __init__(self, target:Target, use_hipcc=False): # gfx942 => MI300, gfx1100 => RX 7900, gfx1201 => RX 9700
super().__init__(target)
from tinygrad.runtime.support.compiler_amd import HIPCompiler
self.compiler, self.tensor_cores = HIPCompiler(target.arch), tc.get_amd(target.arch)
from tinygrad.runtime.support.compiler_amd import HIPCompiler, HIPCCCompiler
self.compiler, self.tensor_cores = (HIPCCCompiler if use_hipcc else HIPCompiler)(target.arch), tc.get_amd(target.arch)
if not self.is_cdna4(target.arch): self.extra_matcher += pm_manual_bf16_cast + extra_pm
if self.is_cdna(target.arch):
self.string_rewrite = PatternMatcher([
@@ -512,7 +512,7 @@ class HIPRenderer(CStyleLanguage):
def asm(self, prg:UOp, lin:UOp) -> bytes:
from tinygrad.renderer.amd.elf import assemble_linear
return assemble_linear(self, prg, lin)
return assemble_linear(prg, lin, self.target.arch)
def render_vector_prefix(self, dtype:DType) -> str:
vec, scal = self.render_dtype(dtype), self.render_dtype(dtype.scalar())
@@ -560,10 +560,7 @@ class HIPRenderer(CStyleLanguage):
return super().render_kernel(function_name, kernel, bufs, uops, prefix)
class HIPCCRenderer(HIPRenderer):
def __init__(self, target:Target):
super().__init__(target)
from tinygrad.runtime.support.compiler_amd import HIPCCCompiler
self.compiler = HIPCCCompiler(target.arch)
def __init__(self, target:Target): super().__init__(target, use_hipcc=True)
class QCOMCLRenderer(OpenCLRenderer):
def __init__(self, target:Target):
+3 -1
View File
@@ -238,7 +238,9 @@ class AMDLLVMRenderer(LLVMRenderer):
(UPat(Ops.LOG2, dtype=dtypes.double, src=(UPat.var("d"),)), xlog2),
(UPat(Ops.EXP2, dtype=dtypes.double, src=(UPat.var("d"),)), xexp2),
])
def asm(self, prg: UOp, lin: UOp) -> bytes: return HIPRenderer(self.target).asm(prg, lin)
def asm(self, prg: UOp, lin: UOp) -> bytes:
from tinygrad.renderer.amd.elf import assemble_linear
return assemble_linear(prg, lin, self.target.arch)
def render(self, uops: list[UOp]) -> str:
prefix = ["""define i8 @f32_to_fp8(float %val, i1 %is_bf8) {
entry: %ival = bitcast float %val to i32\n %exp = and i32 %ival, 2139095040\n %is_special = icmp eq i32 %exp, 2139095040
+3 -2
View File
@@ -6,7 +6,7 @@ from tinygrad.renderer.cstyle import CUDARenderer, OpenCLRenderer
from tinygrad.uop.ops import GroupOp, Ops, UOp, PatternMatcher, UPat, range_str
from tinygrad.runtime.autogen import mesa
from tinygrad.runtime.support.c import POINTER
import base64, ctypes, ctypes.util, struct, functools, inspect, contextlib, itertools
import base64, ctypes, ctypes.util, struct, functools, inspect, itertools
def g(s:str): return getattr(mesa, s)
def nsrc(d:mesa.nir_def) -> mesa.nir_src: return mesa.nir_src(ssa=ctypes.pointer(d))
@@ -169,9 +169,10 @@ class NIRRenderer(Renderer):
self.compiler = fromimport("tinygrad.runtime.support.compiler_mesa", self.__class__.__name__.replace("Renderer", "Compiler"))(target.arch)
if hasattr(self.compiler, "nir_options"): self.nir_options = self.compiler.nir_options
mesa.glsl_type_singleton_init_or_ref()
self._deinit_types = True
def __del__(self):
with contextlib.suppress(AttributeError): mesa.glsl_type_singleton_decref()
if getattr(self, "_deinit_types", False): mesa.glsl_type_singleton_decref()
def param(self, b:mesa.nir_builder, x, sz:int) -> mesa.nir_def: raise NotImplementedError("needs param")
def prerender(self, uops:list[UOp]):
+7 -3
View File
@@ -3,14 +3,18 @@ from tinygrad.device import Compiled, Allocator
from tinygrad.engine.jit import MultiGraphRunner
from tinygrad.renderer import Renderer, cstyle, nir, ptx, llvmir, wgsl
from tinygrad.renderer.cstyle import CStyleLanguage
from tinygrad.uop.ops import Ops
from tinygrad.helpers import cpu_profile, getenv, NULL_ALLOW_COPYOUT
from tinygrad.uop.ops import UOp, Ops
from tinygrad.helpers import cpu_profile, getenv, dedup, NULL_ALLOW_COPYOUT
class NullRenderer(CStyleLanguage):
has_local = False
float4 = "float4"
barrier = "// BARRIER"
code_for_op = {**CStyleLanguage.code_for_op, Ops.THREEFRY: lambda a,b,dtype: f"threefry({a},{b})", Ops.MAX: lambda a,b,dtype: f"max({a},{b})"}
def asm(self, prg: UOp, lin: UOp) -> bytes:
assert self.target.arch.startswith("gfx"), "only amd supports assembly"
from tinygrad.renderer.amd.elf import assemble_linear
return assemble_linear(prg, lin, self.target.arch)
class NullProgram:
def __init__(self, device:str, name:str, lib:bytes, *args, **kwargs): self.device, self.name = device, name
@@ -35,4 +39,4 @@ class NullDevice(Compiled):
"EMULATE is deprecated, use DEV=NULL:HIP:"+{"AMD":"gfx1100", "AMD_RDNA4":"gfx1201", "AMD_CDNA4":"gfx950"}.get(emu, "<arch>")
renderers = [NullRenderer] + [r for m in [cstyle, nir, ptx, llvmir, wgsl] for r in m.__dict__.values()
if inspect.isclass(r) and issubclass(r, Renderer)]
super().__init__(device, NullAllocator(self), renderers, functools.partial(NullProgram, device), NullGraph)
super().__init__(device, NullAllocator(self), dedup(renderers), functools.partial(NullProgram, device), NullGraph)
+2 -3
View File
@@ -112,7 +112,7 @@ class DLL(ctypes.CDLL):
if f.read(4) == b'\x7FELF': return str(l)
def __init__(self, nm:str, paths:str|list[str], extra_paths=[], emsg="", **kwargs):
self.nm, self.emsg = nm, emsg
self.nm, self.emsg = nm, emsg or f"try setting {nm.upper()+'_PATH'}?"
if (path:= DLL.findlib(nm, paths if isinstance(paths, list) else [paths], extra_paths if isinstance(extra_paths, list) else [extra_paths])):
if DEBUG >= 3: print(f"loading {nm} from {path}")
try:
@@ -135,6 +135,5 @@ class DLL(ctypes.CDLL):
return wrap
def __getattr__(self, nm):
if self.nm not in self._loaded_:
raise AttributeError(f"failed to load library {self.nm}: " + (self.emsg or f"try setting {self.nm.upper()+'_PATH'}?"))
if self.nm not in self._loaded_: raise AttributeError(f"failed to load library {self.nm}: {self.emsg}")
return super().__getattr__(nm)
+1
View File
@@ -92,6 +92,7 @@ def compile_hip(prg:str, arch="gfx1100", asm=False) -> bytes:
class HIPCompiler(Compiler):
def __init__(self, arch:str):
assert comgr.dll.nm in c.DLL._loaded_, f"comgr not available: {comgr.dll.emsg}"
self.arch = arch
super().__init__(f"compile_hip_{self.arch}")
def compile(self, src:str) -> bytes: