Skip to content

Commit 0f9ebfc

Browse files
fsx950223claude
andcommitted
feat(compile): surface AMDGPU kernel resource-usage info during compile
Parse LLVM's "; Kernel info:" AsmPrinter comment block (NumVgprs, NumSgprs, Occupancy, LDS/scratch usage) out of the ISA text FLYDSL_DUMP_IR already dumps, and print it during compilation. Requires the companion ROCDL AsmVerbose patch in llvm-project for the comment block to be emitted; the parser degrades gracefully to an empty dict otherwise. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
1 parent f5a6a63 commit 0f9ebfc

3 files changed

Lines changed: 110 additions & 0 deletions

File tree

python/flydsl/compiler/jit_function.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
from .._mlir.passmanager import PassManager
2525
from ..expr.typing import Constexpr, Stream
2626
from ..utils import env, log
27+
from ..utils.kernel_info import parse_kernel_info
2728
from .ast_rewriter import ASTRewriter
2829
from .backends import compile_backend_name, get_backend
2930
from .jit_argument import convert_to_jit_arguments, is_type_param_annotation, resolve_signature
@@ -698,6 +699,11 @@ def _dump_isa(*, dump_dir: Path, ctx: ir.Context, asm: str, verify: bool, stage_
698699
dump_dir.mkdir(parents=True, exist_ok=True)
699700
out = dump_dir / f"{stage_name}.s"
700701
out.write_text(isa_text, encoding="utf-8")
702+
703+
kernel_info = parse_kernel_info(isa_text)
704+
if kernel_info:
705+
print(f"[flydsl.compile] kernel info: {kernel_info}")
706+
701707
return out
702708
except Exception as exc:
703709
log().debug(f"[dump_isa] failed: {exc}")

python/flydsl/utils/kernel_info.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
# Copyright (c) 2025 FlyDSL Project Contributors
3+
"""Parse the LLVM AMDGPU AsmPrinter "Kernel info" comment block.
4+
5+
When LLVM lowers a kernel to AMDGPU assembly, it emits an exact,
6+
compiler-computed resource-usage summary as a comment block, e.g.::
7+
8+
; Kernel info:
9+
; NumVgprs: 6
10+
; NumAgprs: 0
11+
; Occupancy: 8
12+
; LDSByteSize: 0 bytes/workgroup (compile time only)
13+
...
14+
15+
This is only emitted when the target machine is built with ``AsmVerbose``
16+
on. FlyDSL's ROCDL serializer (``mlir/lib/Target/LLVM/ROCDL/Target.cpp``,
17+
``SerializeGPUModuleBase::getTargetOptions()``) enables it, so
18+
``jit_function._dump_isa``'s ``.s`` output always carries this block.
19+
20+
This module parses that block into a plain dict, so callers don't have to
21+
re-derive occupancy/register usage from device properties themselves.
22+
"""
23+
24+
from typing import Dict
25+
26+
KERNEL_INFO_HEADER = "; Kernel info:"
27+
28+
29+
def parse_kernel_info(isa_text: str) -> Dict[str, str]:
30+
"""Parse the ``; Kernel info:`` comment block out of AMDGPU ISA text.
31+
32+
Returns an empty dict if the block is not present (e.g. non-AMDGPU ISA,
33+
or a source that didn't come from asm-verbose codegen).
34+
"""
35+
block_start = isa_text.find(KERNEL_INFO_HEADER)
36+
if block_start == -1:
37+
return {}
38+
39+
block_end = isa_text.find("\n\t", block_start)
40+
if block_end == -1:
41+
block_end = len(isa_text)
42+
43+
info: Dict[str, str] = {}
44+
for line in isa_text[block_start:block_end].splitlines()[1:]:
45+
line = line.lstrip(";").strip()
46+
if ":" not in line:
47+
continue
48+
key, _, value = line.partition(":")
49+
info[key.strip()] = value.strip()
50+
return info

tests/unit/test_kernel_info.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
# Copyright (c) 2025 FlyDSL Project Contributors
3+
4+
"""Unit tests for flydsl.utils.kernel_info's AMDGPU ISA "Kernel info" parser."""
5+
6+
import pytest
7+
8+
from flydsl.utils.kernel_info import get_occupancy, parse_kernel_info
9+
10+
pytestmark = [pytest.mark.l0_backend_agnostic]
11+
12+
SAMPLE_ISA = """\
13+
.text
14+
.globl add_kernel
15+
; Kernel info:
16+
; codeLenInByte = 72
17+
; TotalNumSgprs: 14
18+
; NumVgprs: 6
19+
; NumAgprs: 0
20+
; TotalNumVgprs: 6
21+
; ScratchSize: 0
22+
; MemoryBound: 1
23+
; FloatMode: 240
24+
; IeeeMode: 1
25+
; LDSByteSize: 0 bytes/workgroup (compile time only)
26+
; SGPRBlocks: 1
27+
; VGPRBlocks: 0
28+
; NumSGPRsForWavesPerEU: 14
29+
; NumVGPRsForWavesPerEU: 6
30+
; AccumOffset: 8
31+
; Occupancy: 8
32+
; WaveLimiterHint : 1
33+
; COMPUTE_PGM_RSRC2:SCRATCH_EN: 0
34+
; COMPUTE_PGM_RSRC2:USER_SGPR: 2
35+
.section .rodata
36+
"""
37+
38+
39+
def test_parse_kernel_info_extracts_all_fields():
40+
info = parse_kernel_info(SAMPLE_ISA)
41+
assert info["NumVgprs"] == "6"
42+
assert info["NumAgprs"] == "0"
43+
assert info["Occupancy"] == "8"
44+
assert info["LDSByteSize"] == "0 bytes/workgroup (compile time only)"
45+
assert info["TotalNumSgprs"] == "14"
46+
47+
48+
def test_parse_kernel_info_missing_block_returns_empty():
49+
assert parse_kernel_info("no kernel info here") == {}
50+
51+
52+
def test_get_occupancy():
53+
assert get_occupancy(SAMPLE_ISA) == 8
54+
assert get_occupancy("nothing") is None

0 commit comments

Comments
 (0)