Skip to content

Linalg/XeGPU llama3 forward-pass example - #235

Open
nbpatel wants to merge 1 commit into
llvm:mainfrom
nbpatel:llama
Open

Linalg/XeGPU llama3 forward-pass example#235
nbpatel wants to merge 1 commit into
llvm:mainfrom
nbpatel:llama

Conversation

@nbpatel

@nbpatel nbpatel commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

This PR adds a Llama-3 model script under examples/xegpu, a full Llama-3-style transformer forward pass (6 layers, C=256, H=4 query heads / n_kv=2 KV heads, head_size=64, hidden=1024, T=256) running end-to-end on the Intel GPU via the XeGPU lowering path.

Assisted by Claude

@charithaintc charithaintc left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looks good. I will wait for other reviews.

Comment thread examples/xegpu/llama3.py
return x / (1.0 + np.exp(-x))


def _rope_tables(T, hs, theta=10000.0):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is the meaning of _ prefix? not sure LH use it.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not in examples, no. I don't mind the naming style, either is fine.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

worth checking if the existing test using this function runs OK e2e.

@nbpatel
nbpatel marked this pull request as ready for review July 28, 2026 02:17

@tkarna tkarna left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good overall, minor comments. The lowering schedule seems quite hard-coded/brittle but we can improve it later on.

Comment on lines +94 to +97
# ---- matmul: a(M,K) f16 @ b(K,N) f16 -> (M,N) f32 buffer ----
def matmul(self, a, b, M, N, out_buf=None):
# Standard C = A @ B. `times_weights` emits linalg.matmul; we first fill the
# accumulator with 0. f16 inputs, f32 output -- matches the DPAS hardware.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please use standard python docstrings, here and elsewhere, e.g.

def function(a, b):
    """Short description
    
    Detailed description.
    """

Comment on lines +67 to +71
'mm' = matmul (linalg.matmul) -> DPAS systolic-array kernel
'rms' = RMSNorm (2 generics + 1 fill) -> reduction kernel (uses shared mem)
'fa' = flash multi-head attention -> one kernel (QK^T->softmax->@V,
online-softmax over K/V tiles; causal mask added by the schedule).
'ew' = elementwise (cast / silu / mul / residual) -> row-parallel kernel

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make the kinds strings more human readable, e.g. "mm" -> "matmul", like you've already spelled then out in the llama3_schedule.py header.

from llama3_payload import F32


def _tile_one_matmul(matmul_op, anytype, mm_params):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this function have anytype as an arg? It's not used anywhere?

lh_transform.tile(wg_matmul, tile_sizes=[0, 0, mm_params["k_tile"]])


def _tile_one_fused_attention_region(anytype, qkt_bmm, pv_bmm, softmax_op, fa_params):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

anytype is an arg here as well but we can just create it where needed, or define one in the module.

lh_transform.tile(wg_matmul, tile_sizes=[0, 0, mm_params["k_tile"]])


def _tile_one_fused_attention_region(anytype, qkt_bmm, pv_bmm, softmax_op, fa_params):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Would be good to mention in the docstrings what the input args are. E.g., qkt_bmm is a handle of the QK^T linalg.generic op?

In lighthouse we also tend to use type annotations, so something like

def _tile_one_fused_attention_region(
    qkt_bmm: ir.Value, pv_bmm: ir.Value, softmax_op: ir.Value, fa_params: dict
) -> tuple[ir.Value, ir.Value]

Comment on lines +376 to +378
gen_handles = transform.split_handle(
(anytype,) * ngen_total, match(mod, ops={"linalg.generic"})
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is just match_and_split, no?

Comment on lines +653 to +655
# Fusion leaves the full-size original fill DEAD at func scope (fusion only
# slices a copy inside the forall). It must be removed or the next rms finds too
# many. canonicalize (DCE) at func scope, but never apply_cse at func scope --

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe you could just apply dead code elimination on it own, transform.apply_dce(target) ?

ln_params["T"],
)

# 2) Tile ew generics into own foralls (handles preserved across rms tiling).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIU this creates standalone gpu kernels for the elementwise ops. Could they be fused into their producer or consumer matmuls instead?

Comment thread examples/xegpu/llama3.py
# NUMPY REFERENCE -- the same math in plain numpy, to CHECK the GPU result.
# `_f16` rounds through float16 to model the GPU's f16 matmul precision.
# =============================================================================
def _rms(x, weight, eps=1e-5):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: as these helpers seem to be related to constructing the numpy reference model, maybe add _numpy in the func name to distinguish from the actual graph building methods (or wrap in a class to define a namespace).

Comment thread examples/xegpu/llama3.py
Comment on lines +295 to +303
an=np.ones(C, np.float32),
wq=(np.random.randn(C, C) * sc).astype(np.float16),
wk=(np.random.randn(C, kv_dim) * sc).astype(np.float16),
wv=(np.random.randn(C, kv_dim) * sc).astype(np.float16),
wo=(np.random.randn(C, C) * sc).astype(np.float16),
fn=np.ones(C, np.float32),
w1=(np.random.randn(C, hidden) * sc).astype(np.float16),
w2=(np.random.randn(hidden, C) * sc).astype(np.float16),
w3=(np.random.randn(C, hidden) * sc).astype(np.float16),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: maybe these dict keys could also be made more human readable?

@tkarna tkarna mentioned this pull request Aug 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants