This commit is contained in:
elwrcl committed 2026-06-06 13:25:15 +03:00
1 parent 04d4dc8b09
commit 818cab85dc
12511 files changed
+6156641

No files matched your search

+43
View File
@@ -0,0 +1,43 @@
-- BFI seems available on Gfx9, need to fix the emission code for that.
if devinfo.verx10 < 110 then
error("BFI instruction requires Gfx11+")
end
function BFI_simulation(a, b, c, d)
local width = a & 0x1F
local offset = b & 0x1F
local mask = ((1 << width) - 1) << offset
return ((c << offset) & mask) | (d & ~mask)
end
function BFI(a, b, c, d)
local r = execute {
data = { [0] = a, b, c, d },
src = [[
@id r9
@mov r11 0
@mov r12 1
@mov r13 2
@mov r14 3
@read r1 r11
@read r2 r12
@read r3 r13
@read r4 r14
bfi1 (8) r5 r1 r2 {A@1}
bfi2 (8) r6 r5 r3 r4 {A@1}
@write r9 r6
@eot
]],
}
return r[0]
end
function Hex(v) return string.format("0x%08x", v) end
local a, b, c, d = 12, 12, 0xAAAAAAAA, 0xBBBBBBBB
print("calculated", Hex(BFI(a, b, c, d)))
print("expected", Hex(BFI_simulation(a, b, c, d)))
+62
View File
@@ -0,0 +1,62 @@
if not devinfo.has_bfloat16 then
error("BF16 not supported in this platform.")
end
local r = execute {
src=[[
@id r3
mov (8) r4:f r3 {A@1}
mov (8) r5:f r4.1<0>:f {A@1}
// Converting F to unpacked BF works, but as will be
// illustrated, is not very useful.
mov (8) r10<2>:bf r4:f {A@1}
// With exception of DPAS, instructions need to have at
// least one non-BF operand and the operands must be packed.
mov (8) r11:uw r10<2>:uw {A@1} // Pack it!
add (8) r12:bf r11:bf r4:f {A@1}
// Converting F to packed BF doesn't work, so add the value
// to -0.0f instead. This will preserve the NaN. Note +0.0f
// would not work since it doesn't preserve -0.0f!
mov (8) r20 0x80000000 {A@1} // -0.0f.
add (8) r21:bf r4:f r20:f {A@1} // F -> BF.
// Converting BF to F doesn't work, so for a packed source,
// shift-left the bits to expand it into an UD instead.
shl (8) r30 r21:uw 16:uw {A@1} // BF -> F.
mad (8) r40:bf r12:bf r21:bf r5:f {A@1}
add (8) r41:bf r40:bf r30:f {A@1}
shl (8) r42 r41:uw 16:uw {A@1} // BF -> F.
mov (8) r43 r42:f {A@1}
@write r3 r43
@eot
]]
}
expected = {[0] = 0, 4, 8, 12, 16, 20, 24, 28}
print("result")
dump(r, 8)
print("expected")
dump(expected, 8)
for i=0,7 do
if r[i] ~= expected[i] then
print("FAIL")
return
end
end
print("OK")
+43
View File
@@ -0,0 +1,43 @@
--[[
Execute the example from the Dot Product 4 Accumulate
instruction as seen in the PRM.
mov (1) r1.0:d 0x0102037F:d
// (char4)(0x1,0x2,0x3,0x7F)
mov (1) r2.0:d 50:d
dp4a (1) r3.0:d r2:d r1:d r1:d
// r3.0 = 50 + (0x1*0x1 + 0x2*0x2 + 0x3*0x3 + 0x7F*0x7F)
// = 50 + (1 + 4 + 9 + 16129)
// = 16193
--]]
if devinfo.ver < 12 then
error("DP4A instruction requires Gfx12+")
end
function DP4A(a, b, c)
local r = c
for i = 1, 4 do
r = r + a[i] * b[i]
end
return r
end
local r = execute {
src = [[
@id r9
@mov r1 0x0102037F
@mov r2 50
dp4a (8) r3 r2 r1 r1 {A@1}
@write r9 r3
@eot
]],
}
print("expected", DP4A({1,2,3,0x7F}, {1,2,3,0x7F}, 50))
print("calculated", r[0])
+67
View File
@@ -0,0 +1,67 @@
if not devinfo.has_dpas then
error("DPAS not supported in this platform.")
end
if not devinfo.has_bfloat16 then
error("BF16 not supported in this platform.")
end
local gen = require("mod/gen")
local matrix = require("mod/matrix")
-- Similar to the constants used for VK_KHR_cooperative_matrix.
local M = 8
local N = devinfo.ver >= 20 and 16 or 8
local K = 16
local one_bf = 0x3f80
local A = matrix.new(M, K, one_bf)
local B = matrix.new_diag(K, N, one_bf)
local C = matrix.new(M, N, 0)
-- Note: when tinkering with values, use set() method to set
-- values, e.g.
--
-- A:set(0, 2, 0x4000)
-- Calculate A * B + C. A and B are BF values, C and the result
-- are F values.
local buf = execute {
src =
[[]]
.. gen.mov_grf("bf", 10, A:to_row_major())
-- For `src1`, the source representing the B matrix, DPAS expects
-- the values to be in a layout that looks like an "interleaved"
-- row major. Elements from `packing_factor` rows are packed
-- together.
--
-- See mod/matrix.lua for details on that format.
--
.. gen.mov_grf("bf", 20, B:to_interleaved_row_major(2))
.. gen.mov_grf("f", 30, C:to_row_major())
.. (devinfo.ver >= 20 and [[
dpas.8x8 (16) r40:f r30:f r20:bf r10:bf {A@1,$1}
@syncnop
]] or [[
dpas.8x8 (8) r40:f r30:f r20:bf r10:bf {A@1,$1}
@syncnop
]])
.. gen.write_grfs(40, 8)
.. [[
@eot
]],
}
local r = matrix.from_row_major_buffer(M, N, buf)
r:print("0x%08x")
+63
View File
@@ -0,0 +1,63 @@
if not devinfo.has_dpas then
error("DPAS not supported in this platform.")
end
local gen = require("mod/gen")
local matrix = require("mod/matrix")
-- Similar to the constants used for VK_KHR_cooperative_matrix.
local M = 8
local N = devinfo.ver >= 20 and 16 or 8
local K = 16
local one_hf = 0x3c00
local A = matrix.new(M, K, one_hf)
local B = matrix.new_diag(K, N, one_hf)
local C = matrix.new(M, N, 0)
-- Note: when tinkering with values, use set() method to set
-- values, e.g.
--
-- A:set(0, 2, 0x4000)
-- Calculate A * B + C. A and B are HF values, C and the result
-- are F values.
local buf = execute {
src =
[[]]
.. gen.mov_grf("hf", 10, A:to_row_major())
-- For `src1`, the source representing the B matrix, DPAS expects
-- the values to be in a layout that looks like an "interleaved"
-- row major. Elements from `packing_factor` rows are packed
-- together.
--
-- See mod/matrix.lua for details on that format.
--
.. gen.mov_grf("hf", 20, B:to_interleaved_row_major(2))
.. gen.mov_grf("f", 30, C:to_row_major())
.. (devinfo.ver >= 20 and [[
dpas.8x8 (16) r40:f r30:f r20:hf r10:hf {A@1,$1}
@syncnop
]] or [[
dpas.8x8 (8) r40:f r30:f r20:hf r10:hf {A@1,$1}
@syncnop
]])
.. gen.write_grfs(40, 8)
.. [[
@eot
]],
}
local r = matrix.from_row_major_buffer(M, N, buf)
r:print("0x%08x")
@@ -0,0 +1,61 @@
if not devinfo.has_dpas then
error("DPAS not supported in this platform.")
end
local gen = require("mod/gen")
local matrix = require("mod/matrix")
-- Similar to the constants used for VK_KHR_cooperative_matrix.
local M = 8
local N = devinfo.ver >= 20 and 16 or 8
local K = 32
local A = matrix.new(M, K, 1)
local B = matrix.new_diag(K, N, 1)
local C = matrix.new(M, N, 0)
-- Note: when tinkering with values, use set() method to set
-- values, e.g.
--
-- A:set(0, 2, 0x2)
-- Calculate A * B + C. A and B are UB values, C and the result
-- are UD values.
local buf = execute {
src =
[[]]
.. gen.mov_grf("ub", 10, A:to_row_major())
-- For `src1`, the source representing the B matrix, DPAS expects
-- the values to be in a layout that looks like an "interleaved"
-- row major. Elements from `packing_factor` rows are packed
-- together.
--
-- See mod/matrix.lua for details on that format.
--
.. gen.mov_grf("ub", 20, B:to_interleaved_row_major(4))
.. gen.mov_grf("ud", 30, C:to_row_major())
.. (devinfo.ver >= 20 and [[
dpas.8x8 (16) r40:ud r30:ud r20:ub r10:ub {A@1,$1}
@syncnop
]] or [[
dpas.8x8 (8) r40:ud r30:ud r20:ub r10:ub {A@1,$1}
@syncnop
]])
.. gen.write_grfs(40, 8)
.. [[
@eot
]],
}
r = matrix.from_row_major_buffer(M, N, buf)
r:print()
@@ -0,0 +1,73 @@
-- Xe3 adds a new ARF to store scalar values. It supports only a limited
-- set of operations.
if devinfo.ver < 30 then
error("Scalar register requires Gfx30+")
end
local r = execute {
src=[[
// The new scalar register s0 has 32 bytes.
//
// Only MOV can have scalar as destination. Offsets in scalar
// must be aligned to word boundary. This non-overlapping MOVs
// will fill the first 16 bytes.
mov (1) s0.0:uw 0x1111:uw
mov (1) r2:uw 0x2222:uw
mov (1) s0.2:uw r2<0>:uw {I@1}
mov (1) s0.4:ud 0x33333333:ud {I@1}
mov (4) r4:uw 0x4444:uw
mov (1) s0.8:uq r4<0>:uq {I@1}
// Scalar content can be broadcasted back into GRFs.
// Scalar subregister numbers are byte offsets in gen syntax.
mov (16) r20:ud s0.0<0>:ud
mov (16) r32:ud s0.4<0>:ud
mov (16) r96:ud s0.8<0>:ud
mov (16) r64:ud s0.12<0>:ud
// Scalar can store a list of register numbers to be used as source for
// SEND. Note the scalar will contain the hex value for r11 and r20.
// And the scalar register is indexed as bytes in the send source.
mov (16) r11:ud 0x30000000:ud
mov (1) s0.16:ud 0x0000140b:ud {I@1}
send.ugm (16) null r[s0.16] null:0 0x00000000 0x04000504 {I@1,$1}
// A larger SEND, note that registers for the payload don't need to be
// contiguous, the hardware will gather them together.
add (16) r11:ud r11:ud 0x4:ud {A@1,$1.src}
mov (1) s0.16:ud 0x4060200b:ud
send.ugm (16) null r[s0.16] null:0 0x00000000 0x08002504 {I@1,$1}
@eot
]]
}
expected = {[0] = 0x22221111, 0x33333333, 0x44444444, 0x44444444}
print("result")
dump(r, 4)
print("expected")
dump(expected, 4)
for i=0,3 do
if r[i] ~= expected[i] then
print("FAIL")
return
end
end
print("OK")
@@ -0,0 +1,18 @@
-- Example from the help message.
local r = execute {
data={ [42] = 0x100 },
src=[[
@mov r1 42
@read r2 r1
@id r3
add (8) r4 r2 r3 {A@1}
@write r3 r4
@eot
]]
}
dump(r, 4)
+337
View File
@@ -0,0 +1,337 @@
-- Copyright © 2025 Intel Corporation
-- SPDX-License-Identifier: MIT
local HELP_MESSAGE = [[
Matrix Multiplication using DPAS
Usage: executor matmul.lua FORMAT A_FILE B_FILE [C_FILE]
Perform matrix multiplication D = A * B + C using DPAS instruction.
If C is not provided, will be equivalent to all zeros.
Input files have values separated by spaces, with the rows
separated by newlines. Values are expected to be valid for
the format, e.g. if a matrix should contain UB (unsigned byte),
values should be between 0-255. Float-pointing data is
expected to be in "raw" form as hexadecimal values.
Matrices smaller than the maximum dimensions will be automatically
zero-padded to the required size for DPAS computation.
Maximum dimensions are limited by data format and hardware version.
Gfx20+
- HF/F: A max 8x16, B max 16x16, C/D max 8x16
- BF/F: A max 8x16, B max 16x16, C/D max 8x16
- UB/UD: A max 8x32, B max 32x16, C/D max 8x16
Gfx125
- HF/F: A max 8x16, B max 16x8, C/D max 8x8
- BF/F: A max 8x16, B max 16x8, C/D max 8x8
- UB/UD: A max 8x32, B max 32x8, C/D max 8x8
If `octave` is installed, it will be used to verify the results.
]]
-- TODO: Change this program to load matrix data from memory
-- instead of setting data as immediates in the shader code.
if not devinfo.has_dpas then
print("DPAS not supported on this platform.")
os.exit(1)
end
local verify_results = false
do
local handle = io.popen("which octave 2>/dev/null")
local octave_path = handle:read("*a")
handle:close()
verify_results = octave_path and #octave_path > 0
end
local gen = require("mod/gen")
local matrix = require("mod/matrix")
local fp = require("mod/fp")
local format_ab, format_cd, a_file, b_file, c_file
for i = 1, #arg do
if arg[i] == "--help" or arg[i] == "-h" then
print(HELP_MESSAGE)
os.exit(0)
elseif not format_ab then
local format = arg[i]:lower()
format_ab = format:sub(1, format:find("/") - 1)
format_cd = format:sub(format:find("/") + 1)
elseif not a_file then a_file = arg[i]
elseif not b_file then b_file = arg[i]
elseif not c_file then c_file = arg[i]
end
end
if not format_ab or not format_cd or not a_file or not b_file then
print("Usage: executor matmul.lua FORMAT A_FILE B_FILE [C_FILE]")
print("Use --help for more information")
os.exit(1)
end
if not ((format_ab == "hf" and format_cd == "f") or
(format_ab == "bf" and format_cd == "f") or
(format_ab == "ub" and format_cd == "ud")) then
print("Error: format must be 'hf/f', 'bf/f', or 'ub/ud', got '" .. format_ab .. "/" .. format_cd .. "'")
print("Use --help for more information")
os.exit(1)
end
local function read_matrix(m, filename, max_rows, max_cols)
local file = io.open(filename, "r")
if not file then
error("Failed to open file: " .. filename)
end
-- Read entire file content first
local raw_content = file:read("*all")
file:close()
local rows_data = {}
local cols = nil
-- Parse from the raw content string
for line in raw_content:gmatch("[^\r\n]+") do
local row = {}
for val in line:gmatch("%S+") do
local num = tonumber(val)
if not num then
error(string.format(
"Error reading matrix from '%s': invalid number in row %d: " .. val,
filename, #rows_data + 1))
end
table.insert(row, num)
end
if #row > 0 then
if cols == nil then
cols = #row
elseif #row ~= cols then
error(string.format(
"Error reading matrix from %s: inconsistent number of columns (%d) in row %d",
filename, #row, #rows_data + 1))
end
table.insert(rows_data, row)
end
end
local rows = #rows_data
if rows > max_rows then
error(string.format(
"Error reading matrix from %s: too many rows (%d), maximum is %d",
filename, rows, max_rows))
end
if cols > max_cols then
error(string.format(
"Error reading matrix from %s: too many columns (%d), maximum is %d",
filename, cols, max_cols))
end
for i = 1, rows do
for j = 1, cols do
-- Matrix indices are zero indexed.
m:set(i-1, j-1, rows_data[i][j])
end
end
return rows, cols, raw_content
end
local exec_size = devinfo.ver >= 20 and 16 or 8
local packing_factor
if format_ab == "hf" or format_ab == "bf" then
packing_factor = 2
elseif format_ab == "ub" then
packing_factor = 4
end
local max_a = { rows = 8, cols = packing_factor * 8 }
local max_b = { rows = packing_factor * 8, cols = exec_size }
local max_c = { rows = 8, cols = exec_size }
local a = matrix.new(max_a.rows, max_a.cols, 0)
local b = matrix.new(max_b.rows, max_b.cols, 0)
local c = matrix.new(max_c.rows, max_c.cols, 0)
local actual_a_rows, actual_a_cols, a_raw_content = read_matrix(a, a_file, max_a.rows, max_a.cols)
local actual_b_rows, actual_b_cols, b_raw_content = read_matrix(b, b_file, max_b.rows, max_b.cols)
if actual_a_cols ~= actual_b_rows then
error(string.format(
"Matrix dimension mismatch: A is %dx%d, B is %dx%d. A columns (%d) must equal B rows (%d)",
actual_a_rows, actual_a_cols, actual_b_rows, actual_b_cols, actual_a_cols, actual_b_rows))
end
local actual_c_rows, actual_c_cols, c_raw_content
if c_file then
actual_c_rows, actual_c_cols, c_raw_content = read_matrix(c, c_file, max_c.rows, max_c.cols)
if actual_a_rows ~= actual_c_rows or actual_b_cols ~= actual_c_cols then
error(string.format(
"Matrix dimension mismatch: A*B would be %dx%d, but C is %dx%d",
actual_a_rows, actual_b_cols, actual_c_rows, actual_c_cols))
end
else
-- C defaults to zeros with dimensions matching A*B result
actual_c_rows, actual_c_cols = actual_a_rows, actual_b_cols
c_raw_content = nil
end
local exec_size = c.cols
local encode = function(m, fmt)
local f = nil
if fmt == "hf" then f = fp.encode_f16
elseif fmt == "bf" then f = fp.encode_bf16
elseif fmt == "f" then f = fp.encode_f32
end
if f then
m:apply(f)
end
end
encode(a, format_ab)
encode(b, format_ab)
encode(c, format_cd)
local buf = execute {
src =
[[]]
.. gen.mov_grf(format_ab, 10, a:to_row_major())
.. gen.mov_grf(format_ab, 20, b:to_interleaved_row_major(packing_factor))
.. gen.mov_grf(format_cd, 30, c:to_row_major())
.. string.format([[
dpas.8x8 (%d) r40:%s r30:%s r20:%s r10:%s {A@1,$1}
@syncnop
]], exec_size, format_cd, format_cd, format_ab, format_ab)
.. gen.write_grfs(40, 8)
.. [[
@eot
]],
}
local d = matrix.from_row_major_buffer(8, exec_size, buf)
local d_print_fmt = nil
if string.find(format_cd, "f") then
d_print_fmt = "%.6f"
local f = nil
if format_cd == "hf" then f = fp.decode_f16
elseif format_cd == "bf" then f = fp.decode_bf16
elseif format_cd == "f" then f = fp.decode_f32
else
error("unsupported float format")
end
d:apply(f)
end
-- Just consider the actual rows, same as C matrix.
d:print_submatrix(actual_c_rows, actual_c_cols, d_print_fmt)
--
-- VERIFICATION USING OCTAVE.
--
if verify_results then
local function save_matrix_to_temp(m, rows, cols)
local filename = os.tmpname()
local file = io.open(filename, "w")
for i = 0, rows - 1 do
local row = {}
for j = 0, cols - 1 do
table.insert(row, tostring(m:get(i, j)))
end
file:write(table.concat(row, " ") .. "\n")
end
file:close()
return filename
end
local function save_raw_content_to_temp(raw_content)
local filename = os.tmpname()
local file = io.open(filename, "w")
file:write(raw_content)
file:close()
return filename
end
-- Save A and B raw contents to temp files for Octave
local a_for_octave = save_raw_content_to_temp(a_raw_content)
local b_for_octave = save_raw_content_to_temp(b_raw_content)
local d_for_octave = save_matrix_to_temp(d, actual_c_rows, actual_c_cols)
local c_load, c_for_octave
if c_raw_content then
c_for_octave = save_raw_content_to_temp(c_raw_content)
c_load = string.format("C = single(dlmread('%s'));", c_for_octave)
else
c_load = string.format("C = single(zeros(%d, %d));", actual_c_rows, actual_c_cols)
end
local tolerance = (format_cd == "f") and "1e-3" or "1e-6"
-- TODO: Currently values are rounded to what fits in F (32-bit), but for
-- better results we should have a way to round them to precision based on
-- format, and handle HF and BF16. Octave doesn't support those types
-- natively. See https://github.com/higham/chop for Matlab version of this.
local octave_script = string.format([[
A = single(dlmread('%s'));
B = single(dlmread('%s'));
%s
D = single(dlmread('%s'));
D_expected = A * B + C;
%% Use relative tolerance for better comparison across different magnitudes.
max_val = max(abs(D_expected(:)));
tol = max_val * %s;
if all(all(abs(D_expected - D) < tol))
exit(0);
else
disp('MISMATCH!');
disp('Octave result:');
disp(D_expected);
disp('DPAS result:');
disp(D);
exit(1);
endif
]], a_for_octave, b_for_octave, c_load, d_for_octave, tolerance)
local exit_code = os.execute(
[[octave --quiet --no-gui --eval "]]
.. octave_script ..
[[" 2>&1]])
-- Clean up temporary files
os.remove(a_for_octave)
os.remove(b_for_octave)
if c_for_octave then
os.remove(c_for_octave)
end
os.remove(d_for_octave)
if exit_code then
print("\nMatches Octave.")
else
print("\nMISMATCH with Octave!")
os.exit(1)
end
else
print("\nNOTE: Install `octave` to verify the results.")
end
+128
View File
@@ -0,0 +1,128 @@
-- Copyright © 2025 Intel Corporation
-- SPDX-License-Identifier: MIT
--
-- Encode and decode floating point types.
--
-- Just enough to get basic usage of the examples. If this get serious might
-- be a good idea to implement it using the existing Mesa routines and exposing
-- as part of executor API.
local M = {}
M.decode_float = function(bits, mantissa_bits, exponent_bits, bias)
local total_bits = 1 + exponent_bits + mantissa_bits
local sign_mask = 1 << (total_bits - 1)
local exponent_mask = ((1 << exponent_bits) - 1) << mantissa_bits
local mantissa_mask = (1 << mantissa_bits) - 1
local sign = (bits & sign_mask) ~= 0
local exponent = (bits & exponent_mask) >> mantissa_bits
local mantissa = bits & mantissa_mask
if exponent == 0 then
if mantissa == 0 then
return sign and "-0.0" or "0.0"
else
-- Subnormal number. They don't have implicit leading 1, so
-- the number corresponds to "0.mantissa * 2^(1-bias)".
local value = mantissa / (1 << mantissa_bits)
value = value * (2 ^ (1 - bias))
if sign then value = -value end
return string.format("%.17g", value)
end
elseif exponent == (1 << exponent_bits) - 1 then
if mantissa == 0 then
return sign and "-inf" or "inf"
else
return "nan"
end
else
-- Normal numbers have implicit leading 1, so the
-- number corresponds to "1.mantissa * 2^(exponent-bias)".
local value = 1.0 + (mantissa / (1 << mantissa_bits))
value = value * (2 ^ (exponent - bias))
if sign then value = -value end
return string.format("%.17g", value)
end
end
M.decode_f32 = function(bits)
return M.decode_float(bits, 23, 8, 127)
end
M.decode_f16 = function(bits)
return M.decode_float(bits, 10, 5, 15)
end
M.decode_bf16 = function(bits)
return M.decode_float(bits, 7, 8, 127)
end
M.encode_float = function(value_str, mantissa_bits, exponent_bits, bias)
local value = tonumber(value_str)
if not value then
return nil
end
local total_bits = 1 + exponent_bits + mantissa_bits
local max_exponent = (1 << exponent_bits) - 1
local sign_bit = value < 0 and (1 << (total_bits - 1)) or 0
local signed_inf = sign_bit | max_exponent << mantissa_bits
-- Handle various special cases first: signed zero, NaN, Inf/-Inf.
if value == 0.0 then
return sign_bit
elseif value ~= value then
return (1 << (total_bits - 1)) | (max_exponent << mantissa_bits) | 1
elseif value == math.huge or value == -math.huge then
return signed_inf
end
-- Do math with the absolute value from now on.
if sign_bit ~= 0 then value = -value end
local exponent = math.floor(math.log(value) / math.log(2))
local mantissa_value = value / (2 ^ exponent) - 1.0
exponent = exponent + bias
if exponent <= 0 then
-- Subnormal: no implicit leading 1, use minimum exponent
mantissa_value = value / (2 ^ (1 - bias))
exponent = 0
elseif exponent >= max_exponent then
-- Value too large to represent.
return signed_inf
end
local mantissa = math.floor(mantissa_value * (1 << mantissa_bits) + 0.5)
if mantissa >= (1 << mantissa_bits) then
-- Rounding caused mantissa to overflow, increment exponent.
mantissa = 0
exponent = exponent + 1
if exponent >= max_exponent then
-- Value too large to represent.
return signed_inf
end
end
return sign_bit | (exponent << mantissa_bits) | mantissa
end
M.encode_f32 = function(value_str)
return M.encode_float(value_str, 23, 8, 127)
end
M.encode_f16 = function(value_str)
return M.encode_float(value_str, 10, 5, 15)
end
M.encode_bf16 = function(value_str)
return M.encode_float(value_str, 7, 8, 127)
end
return M
+64
View File
@@ -0,0 +1,64 @@
-- Copyright © 2025 Intel Corporation
-- SPDX-License-Identifier: MIT
local M = {}
local GRF_SLOTS = devinfo.ver >= 20 and 16 or 8
local mov_ub = function(grf, values)
local s = ""
for i = 1, #values, 4 do
local b0 = values[i] or 0
local b1 = values[i+1] or 0
local b2 = values[i+2] or 0
local b3 = values[i+3] or 0
local packed = b0 + (b1 * 2^8) + (b2 * 2^16) + (b3 * 2^24)
local dword_idx = (i-1) // 4
local nr = dword_idx // GRF_SLOTS
local off = dword_idx % GRF_SLOTS
s = s .. string.format("mov (1) r%d.%d 0x%x {A@1}\n", grf + nr, off, packed)
end
return s
end
M.mov_grf = function(fmt, grf, values)
if fmt == "ub" then
return mov_ub(grf, values)
end
local packing = 0
local dst_type = fmt
local imm_type = fmt
if fmt == "hf" then packing = 2
elseif fmt == "bf" then packing = 2; dst_type = "uw"; imm_type = "uw"
elseif fmt == "f" then packing = 1
elseif fmt == "ud" then packing = 1
else
error("unsupported format")
end
local s = ""
for i, v in ipairs(values) do
nr = (i-1) // (GRF_SLOTS * packing)
off = (i-1) % (GRF_SLOTS * packing)
s = s .. string.format("mov (1) r%d.%d:%s 0x%x:%s {A@1}\n",
grf + nr, off, dst_type, v, imm_type)
end
return s
end
M.write_grfs = function(grf, count)
local s = "@id r126\n"
local width = devinfo.ver >= 20 and 16 or 8
for i = 1, count do
s = s .. string.format([[
@write r126 r%d
add (%d) r126 r126 %d {A@1}
]], grf + i - 1, width, GRF_SLOTS)
end
return s
end
return M
+151
View File
@@ -0,0 +1,151 @@
-- Copyright © 2025 Intel Corporation
-- SPDX-License-Identifier: MIT
local M = {}
local Matrix = {}
Matrix.__index = Matrix
Matrix.get = function(self, i, j)
if i < 0 or i >= self.rows or j < 0 or j >= self.cols then
error(string.format("Index out of bounds in get(): i=%d j=%d (size %dx%d)",
i, j, self.rows, self.cols))
end
return self.data[i][j]
end
Matrix.set = function(self, i, j, value)
if i < 0 or i >= self.rows or j < 0 or j >= self.cols then
error(string.format("Index out of bounds in set(): i=%d j=%d (size %dx%d)",
i, j, self.rows, self.cols))
end
self.data[i][j] = value
end
Matrix.print_submatrix = function(self, rows, cols, fmt)
local fmt = fmt or "%4u"
print(string.format("# %dx%d matrix", rows, cols))
io.write("[\n")
for i = 0, rows - 1 do
for j = 0, cols - 1 do
io.write(string.format(fmt, self.data[i][j]))
if j < cols - 1 then io.write(" ") end
end
io.write("\n")
end
io.write("]\n")
end
Matrix.print = function(self, fmt)
self:print_submatrix(self.rows, self.cols, fmt)
end
-- "Interleaved" row major is like row major except that
-- elements from `packing_factor` rows are packed together.
--
-- So, for a 4x4 matrix with packing factor 2
--
-- [ 1 2 3 4
-- 5 6 7 8
-- 9 a b c
-- d e f 0 ]
--
-- in regular row major the sequence
--
-- [ 1 2 3 4 5 6 7 8 9 a b c d e f 0 ]
--
-- while in "interleaved" row major
--
-- [ 1 5 2 6 3 7 4 8 9 d a e b f c 0 ]
--
-- This is used by `src1` of DPAS instruction.
--
Matrix.to_interleaved_row_major = function(self, packing_factor)
local result = {}
for row = 0, self.rows-1 do
for col = 0, self.cols-1 do
local v = self.data[row][col]
local idx = (row // packing_factor) * (self.cols * packing_factor) +
col * packing_factor +
row % packing_factor
-- The +1 is for 1-based arrays in Lua.
result[idx+1] = v
end
end
return result
end
Matrix.to_row_major = function(self)
local result = {}
for i = 0, self.rows - 1 do
for j = 0, self.cols - 1 do
local idx = i * self.cols + j
-- The +1 is for 1-based arrays in Lua.
result[idx+1] = self.data[i][j]
end
end
return result
end
M.new = function(rows, cols, default)
local self = setmetatable({}, Matrix)
self.rows = rows
self.cols = cols
self.data = {}
for i = 0, rows - 1 do
self.data[i] = {}
for j = 0, cols - 1 do
self.data[i][j] = default or 0
end
end
return self
end
M.new_diag = function(rows, cols, value)
cols = cols or rows -- If cols not provided, use rows (square matrix)
local self = M.new(rows, cols, 0)
local min_dim = math.min(rows, cols)
for i = 0, min_dim - 1 do
self.data[i][i] = value
end
return self
end
M.from_row_major = function(rows, cols, data)
local self = M.new(rows, cols)
for i = 0, rows - 1 do
for j = 0, cols - 1 do
local idx = i * cols + j + 1
self.data[i][j] = data[idx] or 0
end
end
return self
end
-- Like above but assume data is 0-indexed like a memory buffer.
M.from_row_major_buffer = function(rows, cols, data)
local self = M.new(rows, cols)
for i = 0, rows - 1 do
for j = 0, cols - 1 do
local idx = i * cols + j
self.data[i][j] = data[idx] or 0
end
end
return self
end
Matrix.apply = function(self, func)
for i = 0, self.rows - 1 do
for j = 0, self.cols - 1 do
self.data[i][j] = func(self.data[i][j])
end
end
end
return M
+6
View File
@@ -0,0 +1,6 @@
execute {
src = [[
nop (8)
@eot
]],
}
@@ -0,0 +1,57 @@
if devinfo.ver < 20 then
error("SRND instruction requires Gfx20+")
end
local r = execute {
src = [[
@id r1
// Prepare F32 input data in r2
mov (8) r2.0 0x00000000 {A@1} // 0.0f
mov (8) r2.1 0x80000000 {A@1} // -0.0f
mov (8) r2.2 0x7f7fffff {A@1} // FLT_MAX
mov (8) r2.3 0xff7fffff {A@1} // -FLT_MAX
mov (8) r2.4 0x00800000 {A@1} // smallest normal
mov (8) r2.5 0x7fc00000 {A@1} // NaN
mov (8) r2.6 0x7f800000 {A@1} // +inf
mov (8) r2.7 0xff800000 {A@1} // -inf
mov (8) r3<2>:uw 42:uw {A@1}
// Stochastic rounding: F32 -> HF16 using r3 as random, packed
(W) srnd (8) r4<2>:hf r2:f r3:f {A@1}
// Convert back to F32 for checking, using supported regioning
mov (8) r5:f r4<2>:hf {A@1}
@write r1 r5
@eot
]],
}
print("result")
dump(r, 8)
print("expected")
expected = {
[0] = 0x00000000,
0x80000000,
0x7f800000, -- FLT_MAX rounds to +inf in HF16
0xff800000, -- -FLT_MAX rounds to -inf in HF16
0x00000000, -- smallest F32 normal rounds to zero in HF16
0x7fc00000,
0x7f800000,
0xff800000
}
dump(expected, 8)
for i=0,7 do
if r[i] ~= expected[i] then
print("FAIL at index", i, string.format("got 0x%08x expected 0x%08x", r[i], expected[i]))
return
end
end
print("OK")
@@ -0,0 +1,64 @@
if devinfo.ver < 20 then
error("SRND instruction requires Gfx20+")
end
F32_VALUE = 0x3F8016F0 -- 1.0007
-- F32_VALUE = 0x3F8009D5 -- 1.0003
F16_ROUNDED_UP = 0x3C01 -- 1.001
F16_ROUNDED_DOWN = 0x3C00 -- 1.000
N = 16 * 4
math.randomseed(os.time())
local random_data = {}
for i = 0, N-1 do
random_data[i] = math.random(1 << 32) - 1
end
src = [[
@id r10
@mov r20 ]] .. F32_VALUE .. [[
]]
for i = 0, (N/16)-1 do
src = src .. [[
@read r30 r10
@mov r40 0
(W) srnd (16) r40<2>:hf r20:f r30:f {A@1}
// Change r30 to r40 to see the random values.
@write r10 r40
add (16) r10 r10 0x10 {A@1}
]]
end
src = src .. [[
@eot
]]
local r = execute {
data = random_data,
src = src,
}
up = 0
down = 0
invalid = 0
for i = 0, N-1 do
if r[i] == F16_ROUNDED_UP then
up = up + 1
elseif r[i] == F16_ROUNDED_DOWN then
down = down + 1
else
invalid = invalid + 1
end
end
dump(r, N)
print()
print("rounded up ", up/N, "%")
print("rounded down ", down/N, "%")
print("invalid ", invalid/N, "%")
@@ -0,0 +1,51 @@
-- Illustrates executor parsing of structured control flow without having to
-- spell out JIP/UIP for IF/ELSE/ENDIF. WHILE still needs an explicit JIP,
-- but the common structured branch labels are inferred automatically.
local src = [[
@id r1
@mov r2 0
@mov r3 0
@mov r4 0
LOOP:
// First conditional: 0 < 1, so this takes the then-path.
cmp (1) (lt)f0.0 null:d r4<0>:d 1:d {A@1}
(f0.0) if(1)
add (1) r2 r2<0> 1
else(1)
add (1) r2 r2<0> 2
endif(1)
// Second conditional: 0 < 0 is false, so this takes the else-path.
cmp (1) (lt)f0.0 null:d r4<0>:d 0:d {A@1}
(f0.0) if(1)
add (1) r2 r2<0> 4
else(1)
add (1) r2 r2<0> 8
endif(1)
add (1) r3 r3<0> 1
cmp (1) (lt)f0.0 null:d r3<0>:d 2:d {A@1}
// WHILE still needs an explicit loop-start label for its JIP back-edge.
(f0.0) while(1) jip:LOOP
@write r1 r2
@eot
]]
local r = execute { src = src }
local expected = {[0] = 18}
print("result")
dump(r, 1)
print("expected")
dump(expected, 1)
if r[0] ~= expected[0] then
error(string.format("got %u expected %u", r[0], expected[0]))
end
print("OK")
+20
View File
@@ -0,0 +1,20 @@
local data = {}
for i = 0, 8-1 do
data[i] = i * 4
end
local r = execute {
data = data,
src = [[
@id r1
@read r3 r1
add (8) r3 r3 0x100
@write r1 r3
@eot
]],
}
dump(r, 8)
+179
View File
@@ -0,0 +1,179 @@
#!/bin/bash
cd "$(dirname "${BASH_SOURCE[0]}")"
if ! command -v executor &> /dev/null; then
echo "ERROR: executor command not found." >&2
exit 1
fi
set -e
executor matmul.lua UB/UD \
<(cat <<EOF
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
EOF
) \
<(cat <<EOF
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
EOF
)
executor matmul.lua HF/F \
<(cat <<EOF
0.25 1.75 2.00 3.50 4.00 5.00 6.00 7.00 8.00 9.00 10.00 11.00 12.00 13.00 14.00 15.00
16.00 17.00 18.00 19.00 20.00 21.00 22.75 23.00 24.00 25.00 26.00 27.00 28.00 29.00 30.00 31.00
32.00 33.00 34.00 35.00 36.00 37.00 38.00 39.00 40.00 41.00 42.00 43.00 44.00 45.25 46.00 47.00
48.00 49.00 50.00 51.00 52.00 53.00 54.00 55.00 56.00 57.00 58.00 59.00 60.00 61.00 62.00 63.00
64.00 65.00 66.00 67.00 68.00 69.00 70.00 71.00 72.00 73.00 74.00 75.00 76.00 77.00 78.00 79.00
80.00 81.00 82.00 83.00 84.00 85.00 86.00 87.00 88.00 89.00 90.00 91.00 92.00 93.00 94.00 95.00
96.00 97.00 98.00 99.00 100.0 101.0 102.0 103.0 104.0 105.0 106.0 107.0 108.0 109.0 110.0 111.0
112.00 113.0 114.0 115.0 116.0 117.0 118.0 119.0 120.0 121.0 122.0 123.0 124.0 125.0 126.0 127.0
EOF
) \
<(cat <<EOF
0.00 1.00 2.00 3.00 4.00 5.00 6.00 7.00 8.00 9.25 10.00 11.00 12.00 13.00 14.00 15.00
16.00 17.00 18.00 19.00 20.00 21.00 22.00 23.00 24.00 25.00 26.00 27.00 28.00 29.00 30.00 31.00
32.00 33.00 34.00 35.00 36.00 37.00 38.00 39.00 40.00 41.00 42.00 43.00 44.00 45.00 46.00 47.00
48.00 49.00 50.00 51.00 52.00 53.00 54.00 55.00 56.00 57.00 58.00 59.00 60.00 61.00 62.00 63.00
64.00 65.00 66.00 67.00 68.00 69.00 70.00 71.00 72.00 73.00 74.75 75.00 76.00 77.00 78.00 79.00
80.00 81.00 82.00 83.00 84.00 85.00 86.00 87.00 88.00 89.00 90.00 91.00 92.00 93.00 94.00 95.00
96.00 97.00 98.00 99.00 100.0 101.0 102.0 103.0 104.0 105.0 106.0 107.0 108.0 109.0 110.0 111.0
112.00 113.0 114.0 115.0 116.0 117.0 118.0 119.0 120.0 121.0 122.0 123.0 124.0 125.0 126.0 127.0
128.00 129.0 130.0 131.0 132.0 133.0 134.0 135.0 136.0 137.0 138.0 139.0 140.0 141.0 142.0 143.0
144.00 145.0 146.0 147.0 148.0 149.0 150.0 151.0 152.0 153.0 154.0 155.0 156.0 157.0 158.0 159.0
160.00 161.0 162.0 163.0 164.0 165.0 166.0 167.0 168.0 169.0 170.0 171.0 172.0 173.0 174.0 175.0
176.00 177.0 178.0 179.0 180.0 181.0 182.0 183.0 184.0 185.0 186.0 187.0 188.0 189.0 190.0 191.0
192.00 193.0 194.0 195.0 196.0 197.0 198.0 199.0 200.0 201.0 202.0 203.0 204.0 205.0 206.0 207.0
208.00 209.0 210.0 211.0 212.0 213.0 214.0 215.0 216.0 217.0 218.0 219.0 220.0 221.0 222.0 223.0
224.00 225.0 226.0 227.0 228.0 229.0 230.0 231.0 232.0 233.0 234.0 235.0 236.0 237.0 238.0 239.0
240.00 241.0 242.0 243.0 244.0 245.0 246.0 247.0 248.0 249.0 250.0 251.0 252.0 253.0 254.0 255.0
EOF
)
executor matmul.lua UB/UD \
<(cat <<EOF
0 1 2 3 4 5 6 7
8 9 10 11 12 13 14 15
16 17 18 19 20 21 22 23
24 25 26 27 28 29 30 31
EOF
) \
<(cat <<EOF
1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1
EOF
)
executor matmul.lua HF/F \
<(cat <<EOF
0.25 1.00 2.75 3.00 4.25
5.00 6.75 7.00 8.50 9.00
10.25 11.00 12.75 13.00 14.25
EOF
) \
<(cat <<EOF
0.0 1.25 2.0 3.75 4.0 5.75
6.0 7.25 8.0 9.50 10.0 11.25
12.0 13.75 14.0 15.75 16.0 17.25
18.0 19.50 20.0 21.25 22.0 23.75
24.0 25.75 26.0 27.25 28.0 29.50
EOF
)
executor matmul.lua UB/UD <(echo "22") <(echo "1")
executor matmul.lua BF/F \
<(cat <<EOF
0.25 1.75 2.00 3.50 4.00 5.00 6.00 7.00 8.00 9.00 10.00 11.00 12.00 13.00 14.00 15.00
16.00 17.00 18.00 19.00 20.00 21.00 22.75 23.00 24.00 25.00 26.00 27.00 28.00 29.00 30.00 31.00
32.00 33.00 34.00 35.00 36.00 37.00 38.00 39.00 40.00 41.00 42.00 43.00 44.00 45.25 46.00 47.00
48.00 49.00 50.00 51.00 52.00 53.00 54.00 55.00 56.00 57.00 58.00 59.00 60.00 61.00 62.00 63.00
64.00 65.00 66.00 67.00 68.00 69.00 70.00 71.00 72.00 73.00 74.00 75.00 76.00 77.00 78.00 79.00
80.00 81.00 82.00 83.00 84.00 85.00 86.00 87.00 88.00 89.00 90.00 91.00 92.00 93.00 94.00 95.00
96.00 97.00 98.00 99.00 100.0 101.0 102.0 103.0 104.0 105.0 106.0 107.0 108.0 109.0 110.0 111.0
112.00 113.0 114.0 115.0 116.0 117.0 118.0 119.0 120.0 121.0 122.0 123.0 124.0 125.0 126.0 127.0
EOF
) \
<(cat <<EOF
0.00 1.00 2.00 3.00 4.00 5.00 6.00 7.00 8.00 9.25 10.00 11.00 12.00 13.00 14.00 15.00
16.00 17.00 18.00 19.00 20.00 21.00 22.00 23.00 24.00 25.00 26.00 27.00 28.00 29.00 30.00 31.00
32.00 33.00 34.00 35.00 36.00 37.00 38.00 39.00 40.00 41.00 42.00 43.00 44.00 45.00 46.00 47.00
48.00 49.00 50.00 51.00 52.00 53.00 54.00 55.00 56.00 57.00 58.00 59.00 60.00 61.00 62.00 63.00
64.00 65.00 66.00 67.00 68.00 69.00 70.00 71.00 72.00 73.00 74.75 75.00 76.00 77.00 78.00 79.00
80.00 81.00 82.00 83.00 84.00 85.00 86.00 87.00 88.00 89.00 90.00 91.00 92.00 93.00 94.00 95.00
96.00 97.00 98.00 99.00 100.0 101.0 102.0 103.0 104.0 105.0 106.0 107.0 108.0 109.0 110.0 111.0
112.00 113.0 114.0 115.0 116.0 117.0 118.0 119.0 120.0 121.0 122.0 123.0 124.0 125.0 126.0 127.0
128.00 129.0 130.0 131.0 132.0 133.0 134.0 135.0 136.0 137.0 138.0 139.0 140.0 141.0 142.0 143.0
144.00 145.0 146.0 147.0 148.0 149.0 150.0 151.0 152.0 153.0 154.0 155.0 156.0 157.0 158.0 159.0
160.00 161.0 162.0 163.0 164.0 165.0 166.0 167.0 168.0 169.0 170.0 171.0 172.0 173.0 174.0 175.0
176.00 177.0 178.0 179.0 180.0 181.0 182.0 183.0 184.0 185.0 186.0 187.0 188.0 189.0 190.0 191.0
192.00 193.0 194.0 195.0 196.0 197.0 198.0 199.0 200.0 201.0 202.0 203.0 204.0 205.0 206.0 207.0
208.00 209.0 210.0 211.0 212.0 213.0 214.0 215.0 216.0 217.0 218.0 219.0 220.0 221.0 222.0 223.0
224.00 225.0 226.0 227.0 228.0 229.0 230.0 231.0 232.0 233.0 234.0 235.0 236.0 237.0 238.0 239.0
240.00 241.0 242.0 243.0 244.0 245.0 246.0 247.0 248.0 249.0 250.0 251.0 252.0 253.0 254.0 255.0
EOF
)
executor matmul.lua BF/F \
<(cat <<EOF
1.25 2.75 3.00 4.25
5.00 6.75 7.50 8.00
9.25 10.00 11.75 12.25
EOF
) \
<(cat <<EOF
2.0 1.25 3.75 2.0 1.0
1.0 3.00 2.00 1.50 3.0
3.75 2.00 1.00 3.00 2.0
2.0 1.25 3.00 2.00 1.75
EOF
)
echo ""
echo "All tests passed!"
+108
View File
@@ -0,0 +1,108 @@
/*
* Copyright © 2024 Intel Corporation
* SPDX-License-Identifier: MIT
*/
#ifndef EXECUTOR_H
#define EXECUTOR_H
#include <stdbool.h>
#include <stdint.h>
#include "intel/dev/intel_device_info.h"
#include "intel/isl/isl.h"
#include "perf/intel_perf.h"
#include "perf/intel_perf_query.h"
typedef struct {
uint32_t size;
uint32_t handle;
void *map;
void *cursor;
uint64_t addr;
} executor_bo;
typedef struct {
void *mem_ctx;
struct intel_device_info *devinfo;
struct isl_device *isl_dev;
int fd;
struct {
uint32_t ctx_id;
} i915;
struct {
uint32_t vm_id;
uint32_t queue_id;
} xe;
struct {
executor_bo batch;
executor_bo extra;
executor_bo data;
executor_bo perf;
} bo;
bool perf_enabled;
struct {
struct intel_perf_context *ctx;
struct intel_perf_query_object *obj;
} perf_query;
uint64_t batch_start;
} executor_context;
typedef struct {
const char *original_src;
void *kernel_bin;
uint32_t kernel_size;
} executor_params;
typedef struct {
uint64_t offset;
} executor_address;
__attribute__((unused)) static uint64_t
executor_combine_address(void *data, void *location,
executor_address address, uint32_t delta)
{
return address.offset + delta;
}
executor_address executor_address_of_ptr(executor_bo *bo, void *ptr);
void *executor_alloc_bytes(executor_bo *bo, uint32_t size);
void *executor_alloc_bytes_aligned(executor_bo *bo, uint32_t size, uint32_t alignment);
void failf(const char *fmt, ...) PRINTFLIKE(1, 2);
const char *executor_apply_macros(executor_context *ec, const char *original_src);
#ifdef genX
# include "executor_genx.h"
#else
# define genX(x) gfx9_##x
# include "executor_genx.h"
# undef genX
# define genX(x) gfx11_##x
# include "executor_genx.h"
# undef genX
# define genX(x) gfx12_##x
# include "executor_genx.h"
# undef genX
# define genX(x) gfx125_##x
# include "executor_genx.h"
# undef genX
# define genX(x) gfx20_##x
# include "executor_genx.h"
# undef genX
# define genX(x) gfx30_##x
# include "executor_genx.h"
# undef genX
#endif
#endif /* EXECUTOR_H */
+253
View File
@@ -0,0 +1,253 @@
/*
* Copyright © 2024 Intel Corporation
* SPDX-License-Identifier: MIT
*/
#include "executor.h"
#ifdef HAVE_VALGRIND
#include <valgrind.h>
#include <memcheck.h>
#define VG(x) x
#else
#define VG(x) ((void)0)
#endif
#define __gen_address_type executor_address
#define __gen_combine_address executor_combine_address
#define __gen_user_data void
#include "intel/genxml/gen_macros.h"
#include "intel/genxml/genX_pack.h"
#define __executor_cmd_length(cmd) cmd ## _length
#define __executor_cmd_header(cmd) cmd ## _header
#define __executor_cmd_pack(cmd) cmd ## _pack
#define executor_batch_emit(cmd, name) \
for (struct cmd name = { __executor_cmd_header(cmd) }, \
*_dst = executor_alloc_bytes(&ec->bo.batch, __executor_cmd_length(cmd) * 4); \
__builtin_expect(_dst != NULL, 1); \
({ __executor_cmd_pack(cmd)(0, _dst, &name); \
VG(VALGRIND_CHECK_MEM_IS_DEFINED(_dst, __executor_cmd_length(cmd) * 4)); \
_dst = NULL; \
}))
static void
emit_pipe_control(executor_context *ec)
{
executor_batch_emit(GENX(PIPE_CONTROL), pc) {
#if GFX_VER >= 12
pc.HDCPipelineFlushEnable = true;
#endif
pc.PipeControlFlushEnable = true;
pc.CommandStreamerStallEnable = true;
}
}
void
genX(emit_perf_stall)(executor_context *ec)
{
executor_batch_emit(GENX(PIPE_CONTROL), pc) {
#if GFX_VER >= 12
pc.HDCPipelineFlushEnable = true;
#endif
pc.PipeControlFlushEnable = true;
pc.CommandStreamerStallEnable = true;
pc.StallAtPixelScoreboard = true;
}
}
void
genX(emit_mi_report_perf_count)(executor_context *ec, executor_bo *bo,
uint32_t offset_in_bytes,
uint32_t report_id)
{
executor_batch_emit(GENX(MI_REPORT_PERF_COUNT), mi_rpc) {
mi_rpc.MemoryAddress = (executor_address){bo->addr + offset_in_bytes};
mi_rpc.ReportID = report_id;
}
}
void
genX(store_register_mem)(executor_context *ec, executor_bo *bo,
uint32_t reg, uint32_t reg_size,
uint32_t offset_in_bytes)
{
assert(reg_size == 4 || reg_size == 8);
for (uint32_t i = 0; i < reg_size; i += 4) {
executor_batch_emit(GENX(MI_STORE_REGISTER_MEM), srm) {
srm.RegisterAddress = reg + i;
srm.MemoryAddress = (executor_address){bo->addr + offset_in_bytes + i};
}
}
}
static void
emit_state_base_address(executor_context *ec, uint32_t mocs)
{
/* Use the full address for everything. */
const executor_address base_address = {0};
const uint32_t size = (1 << 20) - 1;
executor_batch_emit(GENX(STATE_BASE_ADDRESS), sba) {
sba.GeneralStateBaseAddress = base_address;
sba.GeneralStateBaseAddressModifyEnable = true;
sba.GeneralStateBufferSize = size;
sba.GeneralStateBufferSizeModifyEnable = true;
sba.GeneralStateMOCS = mocs;
sba.DynamicStateBaseAddress = base_address;
sba.DynamicStateBaseAddressModifyEnable = true;
sba.DynamicStateBufferSize = size;
sba.DynamicStateBufferSizeModifyEnable = true;
sba.DynamicStateMOCS = mocs;
sba.InstructionBaseAddress = base_address;
sba.InstructionBaseAddressModifyEnable = true;
sba.InstructionBufferSize = size;
sba.InstructionBuffersizeModifyEnable = true;
sba.InstructionMOCS = mocs;
sba.IndirectObjectBaseAddress = base_address;
sba.IndirectObjectBaseAddressModifyEnable = true;
sba.IndirectObjectBufferSize = size;
sba.IndirectObjectBufferSizeModifyEnable = true;
sba.IndirectObjectMOCS = mocs;
sba.SurfaceStateMOCS = mocs;
sba.StatelessDataPortAccessMOCS = mocs;
#if GFX_VER >= 11
sba.BindlessSamplerStateMOCS = mocs;
#endif
sba.BindlessSurfaceStateMOCS = mocs;
#if GFX_VERx10 >= 125
sba.L1CacheControl = L1CC_WB;
#endif
};
}
static void
executor_perf_begin(executor_context *ec)
{
if (!intel_perf_begin_query(ec->perf_query.ctx, ec->perf_query.obj))
failf("failed to begin OA performance query");
}
static void
executor_perf_end(executor_context *ec)
{
intel_perf_end_query(ec->perf_query.ctx, ec->perf_query.obj);
}
void
genX(emit_execute)(executor_context *ec, const executor_params *params)
{
uint32_t *kernel = executor_alloc_bytes(&ec->bo.extra, params->kernel_size);
memcpy(kernel, params->kernel_bin, params->kernel_size);
executor_address kernel_addr = executor_address_of_ptr(&ec->bo.extra, kernel);
/* TODO: Let SIMD be a parameter. */
struct GENX(INTERFACE_DESCRIPTOR_DATA) desc = {
.KernelStartPointer = kernel_addr.offset,
.NumberofThreadsinGPGPUThreadGroup = 1,
};
void *b = executor_alloc_bytes_aligned(&ec->bo.batch, 0, 256);
ec->batch_start = executor_address_of_ptr(&ec->bo.batch, b).offset;
emit_pipe_control(ec);
#if GFX_VERx10 < 200
executor_batch_emit(GENX(PIPELINE_SELECT), ps) {
ps.PipelineSelection = GPGPU;
ps.MaskBits = 0x3;
}
emit_pipe_control(ec);
#endif
const uint32_t mocs = isl_mocs(ec->isl_dev, 0, false);
emit_state_base_address(ec, mocs);
#if GFX_VERx10 >= 125
executor_batch_emit(GENX(STATE_COMPUTE_MODE), cm) {
cm.Mask1 = 0xffff;
#if GFX_VERx10 >= 200
cm.Mask2 = 0xffff;
#endif
}
executor_batch_emit(GENX(CFE_STATE), cfe) {
cfe.MaximumNumberofThreads = 64;
}
#else
executor_batch_emit(GENX(MEDIA_VFE_STATE), vfe) {
vfe.NumberofURBEntries = 2;
vfe.MaximumNumberofThreads = 64;
}
#endif
emit_pipe_control(ec);
#if GFX_VERx10 >= 125
struct GENX(COMPUTE_WALKER_BODY) body = {
#if GFX_VERx10 >= 200
.SIMDSize = 1,
.MessageSIMD = 1,
#endif
.ThreadGroupIDXDimension = 1,
.ThreadGroupIDYDimension = 1,
.ThreadGroupIDZDimension = 1,
.ExecutionMask = 0xFFFFFFFF,
.PostSync.MOCS = mocs,
.InterfaceDescriptor = desc,
};
#endif
#if GFX_VERx10 >= 125
if (ec->perf_enabled)
executor_perf_begin(ec);
executor_batch_emit(GENX(COMPUTE_WALKER), cw) {
cw.body = body;
};
if (ec->perf_enabled)
executor_perf_end(ec);
#else
uint32_t *idd = executor_alloc_bytes_aligned(&ec->bo.extra, 8 * 4, 256);
GENX(INTERFACE_DESCRIPTOR_DATA_pack)(NULL, idd, &desc);
executor_address idd_addr = executor_address_of_ptr(&ec->bo.extra, idd);
executor_batch_emit(GENX(MEDIA_INTERFACE_DESCRIPTOR_LOAD), load) {
load.InterfaceDescriptorDataStartAddress = idd_addr.offset,
load.InterfaceDescriptorTotalLength = 8 * 4;
}
if (ec->perf_enabled)
executor_perf_begin(ec);
executor_batch_emit(GENX(GPGPU_WALKER), gw) {
gw.ThreadGroupIDXDimension = 1;
gw.ThreadGroupIDYDimension = 1;
gw.ThreadGroupIDZDimension = 1;
gw.RightExecutionMask = 0xFFFFFFFF;
gw.BottomExecutionMask = 0xFFFFFFFF;
}
executor_batch_emit(GENX(MEDIA_STATE_FLUSH), msf);
if (ec->perf_enabled)
executor_perf_end(ec);
#endif
emit_pipe_control(ec);
executor_batch_emit(GENX(MI_BATCH_BUFFER_END), end);
}
+17
View File
@@ -0,0 +1,17 @@
/*
* Copyright © 2024 Intel Corporation
* SPDX-License-Identifier: MIT
*/
#ifndef EXECUTOR_H
#error This file must be included via executor.h
#endif
void genX(emit_execute)(executor_context *ec, const executor_params *params);
void genX(emit_perf_stall)(executor_context *ec);
void genX(emit_mi_report_perf_count)(executor_context *ec, executor_bo *bo,
uint32_t offset_in_bytes,
uint32_t report_id);
void genX(store_register_mem)(executor_context *ec, executor_bo *bo,
uint32_t reg, uint32_t reg_size,
uint32_t offset_in_bytes);
+384
View File
@@ -0,0 +1,384 @@
/*
* Copyright © 2024 Intel Corporation
* SPDX-License-Identifier: MIT
*/
#include <ctype.h>
#include <stdlib.h>
#include "util/ralloc.h"
#include "executor.h"
static bool
startswith(const char *prefix, const char *s)
{
return !strncmp(prefix, s, strlen(prefix));
}
static char *
skip_prefix(char *prefix, char *start)
{
assert(startswith(prefix, start));
char *c = start += strlen(prefix);
return c;
}
static bool
is_comment(const char *c)
{
assert(c);
return c[0] && c[0] == '/' && c[1] == '/';
}
typedef struct {
char **args;
int count;
} parse_args_result;
static parse_args_result
parse_args(void *mem_ctx, char *c)
{
parse_args_result r = {0};
while (*c) {
/* Skip spaces. */
while (*c && isspace(*c))
c++;
if (!*c || is_comment(c))
break;
char *start = c;
while (*c && !isspace(*c) && !is_comment(c))
c++;
r.args = reralloc_array_size(mem_ctx, r.args, sizeof(char *), r.count + 1);
r.args[r.count++] = ralloc_strndup(mem_ctx, start, c - start);
}
return r;
}
static void
executor_macro_mov(executor_context *ec, char **src, char *line)
{
char *c = skip_prefix("@mov", line);
parse_args_result r = parse_args(ec->mem_ctx, c);
if (r.count != 2)
failf("@mov needs 2 arguments, found %d\n", r.count);
const char *reg = r.args[0];
char *value = r.args[1];
const unsigned width = ec->devinfo->ver >= 20 ? 16 : 8;
if (strchr(value, '.')) {
union {
float f;
uint32_t u;
} val;
val.f = strtof(value, NULL);
ralloc_asprintf_append(src,
"mov (%u) %s:f 0x%08x:f\n",
width, reg, val.u);
} else {
for (char *x = value; *x; x++)
*x = tolower(*x);
ralloc_asprintf_append(src,
"mov (%u) %s %s\n",
width, reg, value);
}
}
static void
executor_macro_syncnop(executor_context *ec, char **src, char *line)
{
switch (ec->devinfo->verx10) {
case 90:
case 110: {
/* Not needed. */
break;
}
case 120: {
ralloc_strcat(src,
"(W) sync.nop (8) null {@1,$1.dst}\n");
break;
}
case 125:
case 200:
case 300: {
ralloc_strcat(src,
"(W) sync.nop (8) null {A@1,$1.dst}\n");
break;
}
default:
UNREACHABLE("invalid gfx version");
}
}
static void
executor_macro_eot(executor_context *ec, char **src, char *line)
{
switch (ec->devinfo->verx10) {
case 90:
case 110: {
ralloc_strcat(src,
"(W) mov (8) r127 r0\n"
"(W) send.ts (8) null r127:1 null:0 0x00000000 0x82000010 {EOT}\n");
break;
}
case 120: {
ralloc_strcat(src,
"(W) mov (8) r127 r0\n"
"(W) send.ts (8) null r127:1 null:0 0x00000000 0x02000000 {@1,EOT}\n");
break;
}
case 125: {
ralloc_strcat(src,
"(W) mov (8) r127 r0\n"
"(W) send.gtwy (8) null r127:1 null:0 0x00000000 0x02000000 {A@1,EOT}\n");
break;
}
case 200:
case 300: {
ralloc_strcat(src,
"(W) mov (16) r127 r0\n"
"(W) send.gtwy (16) null r127:1 null:0 0x00000000 0x02000000 {I@1,EOT}\n");
break;
}
default:
UNREACHABLE("invalid gfx version");
}
}
static void
executor_macro_id(executor_context *ec, char **src, char *line)
{
char *c = skip_prefix("@id", line);
parse_args_result r = parse_args(ec->mem_ctx, c);
if (r.count != 1)
failf("@id needs 1 argument, found %d\n", r.count);
const char *reg = r.args[0];
switch (ec->devinfo->verx10) {
case 90:
case 110:
case 120: {
ralloc_asprintf_append(src,
"(W) mov (8) r127:uw 0x76543210:v\n"
"(W) mov (8) %s r127:uw {@1}\n",
reg);
break;
}
case 125: {
ralloc_asprintf_append(src,
"(W) mov (8) r127:uw 0x76543210:v\n"
"(W) mov (8) %s r127:uw {A@1}\n",
reg);
break;
}
case 200:
case 300: {
ralloc_asprintf_append(src,
"(W) mov (8) r127:uw 0x76543210:v\n"
"(W) add (8) r127.8:uw r127:uw 8:uw {A@1}\n"
"(W) mov (16) %s r127:uw {A@1}\n",
reg);
break;
}
default:
UNREACHABLE("invalid gfx version");
}
}
static void
executor_macro_write(executor_context *ec, char **src, char *line)
{
char *c = skip_prefix("@write", line);
parse_args_result r = parse_args(ec->mem_ctx, c);
if (r.count != 2)
failf("@write needs 2 arguments, found %d\n", r.count);
const char *offset_reg = r.args[0];
const char *data_reg = r.args[1];
assert(ec->bo.data.addr <= 0xFFFFFFFF);
uint32_t base_addr = ec->bo.data.addr;
switch (ec->devinfo->verx10) {
case 90:
case 110:
case 120: {
const char *send_op = ec->devinfo->verx10 < 120 ? "sends.hdc1" : "send.hdc1";
ralloc_asprintf_append(src,
"mul (8) r127 %s 0x4:uw {@1}\n"
"add (8) r127 r127 0x%08x {@1}\n"
"%s (8) null r127:1 %s:1 0x00000040 0x02026efd {@1,$1}\n",
offset_reg, base_addr, send_op, data_reg);
executor_macro_syncnop(ec, src, "@syncnop");
break;
}
case 125: {
ralloc_asprintf_append(src,
"mul (8) r127 %s 0x4:uw {A@1}\n"
"add (8) r127 r127 0x%08x {A@1}\n"
"store.ugm.d32.a32 (8) null r127:1 %s:1 {A@1,$1}\n",
offset_reg, base_addr, data_reg);
executor_macro_syncnop(ec, src, "@syncnop");
break;
}
case 200:
case 300: {
ralloc_asprintf_append(src,
"mul (16) r127 %s 0x4:uw {A@1}\n"
"add (16) r127 r127 0x%08x {A@1}\n"
"store.ugm.d32.a32 (16) null r127:1 %s:1 {A@1,$1}\n",
offset_reg, base_addr, data_reg);
executor_macro_syncnop(ec, src, "@syncnop");
break;
}
default:
UNREACHABLE("invalid gfx version");
}
}
static void
executor_macro_read(executor_context *ec, char **src, char *line)
{
char *c = skip_prefix("@read", line);
parse_args_result r = parse_args(ec->mem_ctx, c);
if (r.count != 2)
failf("@read needs 2 arguments, found %d\n", r.count);
/* Order follows underlying SEND, destination first. */
const char *data_reg = r.args[0];
const char *offset_reg = r.args[1];
assert(ec->bo.data.addr <= 0xFFFFFFFF);
uint32_t base_addr = ec->bo.data.addr;
switch (ec->devinfo->verx10) {
case 90:
case 110:
case 120: {
const char *send_op = ec->devinfo->verx10 < 120 ? "sends.hdc1" : "send.hdc1";
ralloc_asprintf_append(src,
"mul (8) r127 %s 0x4:uw {@1}\n"
"add (8) r127 r127 0x%08x {@1}\n"
"%s (8) %s r127:1 null:0 0x00000000 0x02106efd {@1,$1}\n",
offset_reg, base_addr, send_op, data_reg);
executor_macro_syncnop(ec, src, "@syncnop");
break;
}
case 125: {
ralloc_asprintf_append(src,
"mul (8) r127 %s 0x4:uw {A@1}\n"
"add (8) r127 r127 0x%08x {A@1}\n"
"load.ugm.d32.a32 (8) %s:1 r127:1 {A@1,$1}\n",
offset_reg, base_addr, data_reg);
executor_macro_syncnop(ec, src, "@syncnop");
break;
}
case 200:
case 300: {
ralloc_asprintf_append(src,
"mul (16) r127 %s 0x4:uw {A@1}\n"
"add (16) r127 r127 0x%08x {A@1}\n"
"load.ugm.d32.a32 (16) %s:1 r127:1 {A@1,$1}\n",
offset_reg, base_addr, data_reg);
executor_macro_syncnop(ec, src, "@syncnop");
break;
}
default:
UNREACHABLE("invalid gfx version");
}
}
static char *
find_macro_symbol(char *line)
{
char *c = line;
while (isspace(*c)) c++;
return *c == '@' ? c : NULL;
}
static bool
match_macro_name(const char *name, const char *line)
{
if (!startswith(name, line))
return false;
line += strlen(name);
return !*line || isspace(*line) || is_comment(line);
}
const char *
executor_apply_macros(executor_context *ec, const char *original_src)
{
char *scratch = ralloc_strdup(ec->mem_ctx, original_src);
/* Create a ralloc'ed empty string so can call append to it later. */
char *src = ralloc_strdup(ec->mem_ctx, "");
/* TODO: Create a @send macro for common combinations of MsgDesc. */
static const struct {
const char *name;
void (*func)(executor_context *ec, char **output, char *line);
} macros[] = {
{ "@eot", executor_macro_eot },
{ "@mov", executor_macro_mov },
{ "@write", executor_macro_write },
{ "@read", executor_macro_read },
{ "@id", executor_macro_id },
{ "@syncnop", executor_macro_syncnop },
};
char *next = scratch;
while (next) {
char *line = next;
char *end = line;
while (*end && *end != '\n') end++;
next = *end ? end + 1 : NULL;
*end = '\0';
char *macro = find_macro_symbol(line);
if (!macro) {
ralloc_asprintf_append(&src, "%s\n", line);
} else {
bool found = false;
for (int i = 0; i < ARRAY_SIZE(macros); i++) {
if (match_macro_name(macros[i].name, macro)) {
macros[i].func(ec, &src, macro);
found = true;
break;
}
}
if (!found)
failf("unsupported macro line: %s", macro);
}
}
return src;
}
File diff suppressed because it is too large. Load diff
+57
View File
@@ -0,0 +1,57 @@
# Copyright © 2024 Intel Corporation
# SPDX-License-Identifier: MIT
executor_flags = [
no_override_init_args,
sse2_args,
]
executor_includes = [
inc_include,
inc_src,
inc_intel,
]
executor_hw_libs = []
foreach v: ['90', '110', '120', '125', '200', '300']
executor_hw_libs += static_library(
'executor_hw_ver@0@'.format(v),
['executor_genx.c', gen_xml_pack],
include_directories: [executor_includes],
c_args: [
executor_flags,
'-DGFX_VERx10=@0@'.format(v),
],
gnu_symbol_visibility: 'hidden',
dependencies: [
dep_valgrind,
idep_genxml,
idep_intel_dev,
],
build_by_default : false,
)
endforeach
executor = executable(
'executor',
[
'executor_main.c',
'executor_macros.c',
],
dependencies: [
dep_libdrm,
dep_lua,
dep_valgrind,
idep_genxml,
idep_intel_compiler_gen,
idep_intel_decoder_gen,
idep_intel_dev,
idep_intel_perf,
idep_libintel_common,
],
include_directories: [executor_includes],
link_with: [executor_hw_libs],
c_args: [executor_flags],
gnu_symbol_visibility: 'hidden',
install: true
)