intel: add Jay
Jay is a new SSA-based compiler for Intel GPUs. This is an early
work-in-progress. It isn't ready to ship, but we'd like to move development in
tree rather than rebasing the world every week. Please don't bother testing yet
- we know the status and we're working on it!
Jay's design is similar to other modern NIR backends, particularly ACO, NAK and
AGX. It is fully SSA, deconstructing phis after RA. We use a Colombet register
allocator similar to NAK, allowing us to handle Intel's complex register
regioning restrictions in a straightforward way. Spilling logical registers is
straightforward with Braun-Hack.
Thanks to the SSA-based design, the entire backend is essentially linear time,
regardless of register pressure, addressing brw's excessive compile time when
especially spilling with brw.
In this current early draft, we support a limited subset of all three APIs on
Xe2. A lot works and a lot doesn't. The core compiler is there (spilling,
scoreboarding, SIMD32, etc should more or less work), but there are details to
fill in for both performance and correctness. We essentially pass conformance on
OpenGL ES 3.0 and OpenCL 3.0, and we're busy iterating on Vulkan.
Likewise, additional hardware support will come down the line. There's nothing
fundamentally Xe2-specific here. I just have a Lunarlake laptop on my desk, Ken
has a Battlemage card, and we had to pick _something_ as the first target.
Co-authored-by: Kenneth Graunke <kenneth@whitecape.org>
Signed-off-by: Alyssa Rosenzweig <alyssa.rosenzweig@intel.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/40835>
2025-11-27 17:57:25 -05:00
|
|
|
# Copyright 2026 Intel Corporation
|
|
|
|
|
# SPDX-License-Identifier: MIT
|
|
|
|
|
|
|
|
|
|
from typing import TYPE_CHECKING
|
|
|
|
|
import argparse
|
|
|
|
|
import sys
|
|
|
|
|
|
|
|
|
|
from mako import exceptions
|
|
|
|
|
from mako.template import Template
|
|
|
|
|
|
|
|
|
|
from jay_opcodes import OPCODES
|
|
|
|
|
|
|
|
|
|
if TYPE_CHECKING:
|
|
|
|
|
from jay_opcodes import Opcode
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def infer_type(op: 'Opcode') -> bool:
|
|
|
|
|
return op.has_dest and (set(op.types) <= set(["u1", "u32", "u64"]) or
|
|
|
|
|
op.name == 'mov')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def signature(op: 'Opcode', with_dest: bool = True, with_types: bool = False,
|
|
|
|
|
mode: str = 'prototype', type_: str = 't', src: str = '{}') -> str:
|
|
|
|
|
arr = [('jay_builder *', 'b')]
|
|
|
|
|
|
|
|
|
|
if with_types and len(op.types) > 1 and not infer_type(op):
|
|
|
|
|
arr += [('enum jay_type', type_)]
|
|
|
|
|
|
|
|
|
|
if with_dest and op.has_dest:
|
|
|
|
|
arr += [('jay_def', 'dst')]
|
|
|
|
|
|
|
|
|
|
arr += [('jay_def', src.format(f'src{i}')) for i in range(op.num_srcs)]
|
|
|
|
|
arr += [x for x in op.extra_struct if not x[1].startswith('pad')]
|
|
|
|
|
|
|
|
|
|
return ', '.join([(t + ' ' if mode == 'prototype' else '') + v for t, v in arr])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
TEMPLATE = """
|
|
|
|
|
/*
|
|
|
|
|
* Copyright 2026 Intel Corporation
|
|
|
|
|
* SPDX-License-Identifier: MIT
|
|
|
|
|
*/
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
|
|
#include "jay_private.h"
|
|
|
|
|
|
|
|
|
|
#ifndef NDEBUG
|
|
|
|
|
#define type_assert(op, ...) if (!(__VA_ARGS__)) { fprintf(stderr, "%s does not allow type: ", #op); jay_print_type(stderr, t); fprintf(stderr, "\\n"); } assert(__VA_ARGS__)
|
|
|
|
|
#else
|
|
|
|
|
#define type_assert(...)
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
% for op in opcodes.values():
|
|
|
|
|
<%
|
|
|
|
|
OPCODE = op.name.upper()
|
|
|
|
|
num_srcs = op.num_srcs
|
|
|
|
|
has_dest = op.has_dest
|
|
|
|
|
multi_type = len(op.types) > 1
|
|
|
|
|
info_size = f'sizeof(jay_{op.name}_info)' if op.extra_struct else '0'
|
|
|
|
|
operands = ["dst"] + [f"src{i}" for i in range(num_srcs)]
|
2026-05-13 22:17:34 -07:00
|
|
|
if num_srcs > 0 and op.name != 'gpr_from_ugprs':
|
intel: add Jay
Jay is a new SSA-based compiler for Intel GPUs. This is an early
work-in-progress. It isn't ready to ship, but we'd like to move development in
tree rather than rebasing the world every week. Please don't bother testing yet
- we know the status and we're working on it!
Jay's design is similar to other modern NIR backends, particularly ACO, NAK and
AGX. It is fully SSA, deconstructing phis after RA. We use a Colombet register
allocator similar to NAK, allowing us to handle Intel's complex register
regioning restrictions in a straightforward way. Spilling logical registers is
straightforward with Braun-Hack.
Thanks to the SSA-based design, the entire backend is essentially linear time,
regardless of register pressure, addressing brw's excessive compile time when
especially spilling with brw.
In this current early draft, we support a limited subset of all three APIs on
Xe2. A lot works and a lot doesn't. The core compiler is there (spilling,
scoreboarding, SIMD32, etc should more or less work), but there are details to
fill in for both performance and correctness. We essentially pass conformance on
OpenGL ES 3.0 and OpenCL 3.0, and we're busy iterating on Vulkan.
Likewise, additional hardware support will come down the line. There's nothing
fundamentally Xe2-specific here. I just have a Lunarlake laptop on my desk, Ken
has a Battlemage card, and we had to pick _something_ as the first target.
Co-authored-by: Kenneth Graunke <kenneth@whitecape.org>
Signed-off-by: Alyssa Rosenzweig <alyssa.rosenzweig@intel.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/40835>
2025-11-27 17:57:25 -05:00
|
|
|
uniform = " && " .join([f"jay_is_uniform(src{i})" for i in range(num_srcs)])
|
|
|
|
|
reg_file = f"({uniform}) ? UGPR : GPR"
|
|
|
|
|
else:
|
|
|
|
|
reg_file = "GPR"
|
|
|
|
|
if not op.types:
|
|
|
|
|
continue
|
|
|
|
|
# Ignore the lane index when determining the type of a shuffle
|
|
|
|
|
infer_operands = operands[0:-1] if op.name == "shuffle" else operands
|
|
|
|
|
%>
|
|
|
|
|
static inline jay_inst *
|
|
|
|
|
_jay_${OPCODE}(${signature(op, with_types = True)})
|
|
|
|
|
{
|
|
|
|
|
% if infer_type(op):
|
|
|
|
|
enum jay_type t = jay_num_values(dst) == 2 ? JAY_TYPE_U64 :
|
|
|
|
|
${" && ".join([f"(jay_is_flag({x}) || jay_is_imm({x}))" for x in infer_operands])}
|
|
|
|
|
? JAY_TYPE_U1 : JAY_TYPE_U32;
|
|
|
|
|
% elif multi_type:
|
|
|
|
|
type_assert(${OPCODE}, 0
|
|
|
|
|
% for type in op.types:
|
|
|
|
|
|| t == JAY_TYPE_${type.upper()}
|
|
|
|
|
% endfor
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
% else:
|
|
|
|
|
enum jay_type t = JAY_TYPE_${op.types[0].upper()};
|
|
|
|
|
|
|
|
|
|
% endif
|
|
|
|
|
jay_inst *inst = jay_alloc_inst(b, JAY_OPCODE_${OPCODE}, ${num_srcs}, ${info_size});
|
|
|
|
|
% for _, prop in op.extra_struct:
|
|
|
|
|
% if not prop.startswith('pad'):
|
|
|
|
|
jay_set_${op.name}_${prop}(inst, ${prop});
|
|
|
|
|
% endif
|
|
|
|
|
% endfor
|
|
|
|
|
|
|
|
|
|
inst->type = t;
|
|
|
|
|
% if op.has_dest:
|
|
|
|
|
inst->dst = dst;
|
|
|
|
|
% endif
|
|
|
|
|
% for i in range(num_srcs):
|
|
|
|
|
inst->src[${i}] = src${i};
|
|
|
|
|
% endfor
|
|
|
|
|
|
|
|
|
|
jay_builder_insert(b, inst);
|
|
|
|
|
return inst;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define jay_${OPCODE}(${signature(op, with_types = True, mode = 'call')}) _jay_${OPCODE}(${signature(op, with_types = True, src = 'JAY_BUILD_SRC({})', mode='call')})
|
|
|
|
|
|
|
|
|
|
% for type in op.types:
|
|
|
|
|
static inline ${'jay_def' if op.has_dest else 'void'}
|
|
|
|
|
_jay_${OPCODE}_${type}(${signature(op, with_dest = False)})
|
|
|
|
|
{
|
|
|
|
|
% if op.has_dest:
|
|
|
|
|
jay_def dst = jay_alloc_def(b, ${reg_file}, ${2 if '64' in type else 1});
|
|
|
|
|
%endif
|
|
|
|
|
jay_${OPCODE}(${signature(op, with_types = True, type_ = 'JAY_TYPE_'+type.upper(), mode = 'call')});
|
|
|
|
|
% if op.has_dest:
|
|
|
|
|
return dst;
|
|
|
|
|
% endif
|
|
|
|
|
}
|
|
|
|
|
#define jay_${OPCODE}_${type}(${signature(op, with_dest = False, mode =
|
|
|
|
|
'call')}) _jay_${OPCODE}_${type}(${signature(op, src='JAY_BUILD_SRC({})', mode = 'call', with_dest = False)})
|
|
|
|
|
% endfor
|
|
|
|
|
|
|
|
|
|
% endfor
|
|
|
|
|
|
|
|
|
|
#undef type_assert
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def main() -> int:
|
|
|
|
|
parser = argparse.ArgumentParser()
|
|
|
|
|
parser.add_argument('output', action='store')
|
|
|
|
|
args = parser.parse_args()
|
|
|
|
|
|
|
|
|
|
ops = {op: v for (op, v) in OPCODES.items() if op not in {'cmp', 'send'}}
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
with open(args.output, 'w', encoding='utf-8') as f:
|
|
|
|
|
f.write(Template(TEMPLATE).render(
|
|
|
|
|
opcodes=ops,
|
|
|
|
|
signature=signature,
|
|
|
|
|
infer_type=infer_type))
|
|
|
|
|
except Exception:
|
|
|
|
|
print(exceptions.text_error_template().render())
|
|
|
|
|
return 1
|
|
|
|
|
|
|
|
|
|
return 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
|
sys.exit(main())
|