Gen12LP: post-RA spill/fill propagation corrupts OWord spills
Valutazione
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Idoneità per principianti
- 58/100
Direzione di ricerca
Start with visa/SpillFillPropagation.cpp, especially SpillFillPropagation and getMaxSpillAreaOffset, then trace the caller that updates spill size. Reproduce with the supplied OpenCL kernel on Gen12LP and compare pass dumps with -postRAspillCleanup 0. Done means invalid-offset spill/fill intrinsics no longer merge distinct slots or reduce spill size, while valid-offset propagation still works; also account for OWord push/pop from BoundedRA.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
On platforms without LSC (Gen12LP and older), post-RA spill/fill propagation treats distinct OWord spill slots as the same slot, and the kernel silently returns wrong results.
// Each work-item loads 8 uint16 vectors and keeps them live until the stores
// (`out` may alias `in`), so at SIMD32 some of them get spilled. The private
// array `p` is dynamically indexed, so it lives in scratch: 640 * 4 B * 32 lanes
// = 80 KB per thread, which makes vISA spill with OWord block messages.
#ifndef P
#define P 640
#endif
#define R8(X) X(0) X(1) X(2) X(3) X(4) X(5) X(6) X(7)
#define LOAD(a) uint16 v##a = vload16(a * S + g, in);
#define STORE(a) vstore16(v##a + p[(a * s) % P], (7 - a) * S + g, out);
__attribute__((intel_reqd_sub_group_size(32)))
__kernel void k(__global uint *out, __global const uint *in, uint s) {
uint g = get_global_id(0), S = get_global_size(0);
R8(LOAD)
uint p[P];
for (uint j = 0; j < P; j++)
p[(j * s) % P] = j;
R8(STORE)
}
With 256 work-items in groups of 32 and s = 3 (host program below), on a Tiger Lake Iris Xe with IGC 2.41.5:
device: Intel(R) Iris(R) Xe Graphics, driver 26.35.039758
mismatch: vector 0, item 0, lane 1: got 0x00000000, want 0x9e3779b1
...
FAIL: 29695 of 32768 values wrong
With -DP=613 (78464 B of private memory, just under the threshold below) it passes. The kernel as given passes on an Arc A770 (DG2).
Root cause
When private memory takes at least 60% of the 128 KB that HWord scratch messages can address, GlobalRA::coloringRegAlloc spills with OWord block messages instead. Their offset goes in the message header, so their spill/fill intrinsics carry InvalidOffset.
SpillFillPropagation, added in v2.33.0 and enabled by default through vISA_GRFPostRASpillCodeCleanup, identifies spill slots by the intrinsic offset. These OWord intrinsics all carry InvalidOffset, so the pass treats distinct slots as one. In this reproducer, -dumpPassesAll shows 116 spills and 232 fills after Spill_GRF, but 1 spill and 4 fills after spillFillPropagation, with 228 more movs. getMaxSpillAreaOffset ignores invalid offsets and returns 0, so the caller also reduces the spill size to 0.
With IGC_VISAOptions="-postRAspillCleanup 0", the reproducer passes.
The same bug is on master (0ac43c0cf). DG2 uses LSC instead of OWord spills. The HWord path on Gen12LP has a separate problem, #469.
A fix could skip the propagation, and keep the spill size, whenever a spill/fill intrinsic has no valid offset. That also covers the OWord push/pop that BoundedRA generates.
We hit this in oneAPI.jl (Julia) with a kernel that has a large private array.
host.c
Build with cc host.c -lOpenCL -o host, then run ./host kernel.cl.
#define CL_TARGET_OPENCL_VERSION 300
#include <CL/cl.h>
#include <stdio.h>
#include <stdlib.h>
#define CHECK(x) do { cl_int e_ = (x); if (e_ != CL_SUCCESS) { \
fprintf(stderr, "%s failed: %d\n", #x, e_); exit(1); } } while (0)
int main(int argc, char **argv) {
const char *path = argc > 1 ? argv[1] : "kernel.cl";
const char *opts = argc > 2 ? argv[2] : "";
FILE *f = fopen(path, "rb");
if (!f) { perror(path); return 1; }
fseek(f, 0, SEEK_END); long len = ftell(f); fseek(f, 0, SEEK_SET);
char *src = malloc(len + 1); fread(src, 1, len, f); src[len] = 0; fclose(f);
cl_platform_id plat; cl_device_id dev; cl_int err;
CHECK(clGetPlatformIDs(1, &plat, NULL));
CHECK(clGetDeviceIDs(plat, CL_DEVICE_TYPE_GPU, 1, &dev, NULL));
char name[256]; clGetDeviceInfo(dev, CL_DEVICE_NAME, sizeof name, name, NULL);
char drv[256]; clGetDeviceInfo(dev, CL_DRIVER_VERSION, sizeof drv, drv, NULL);
printf("device: %s, driver %s\n", name, drv);
cl_context ctx = clCreateContext(NULL, 1, &dev, NULL, NULL, &err); CHECK(err);
cl_command_queue q = clCreateCommandQueueWithProperties(ctx, dev, NULL, &err); CHECK(err);
cl_program prog = clCreateProgramWithSource(ctx, 1, (const char **)&src, NULL, &err); CHECK(err);
if (clBuildProgram(prog, 1, &dev, opts, NULL, NULL) != CL_SUCCESS) {
char log[16384]; clGetProgramBuildInfo(prog, dev, CL_PROGRAM_BUILD_LOG, sizeof log, log, NULL);
fprintf(stderr, "build failed:\n%s\n", log); return 1;
}
cl_kernel k = clCreateKernel(prog, "k", &err); CHECK(err);
enum { G = 256, V = 8, W = 16 };
size_t n = (size_t)V * G * W;
cl_uint *in = malloc(n * sizeof *in), *out = calloc(n, sizeof *out);
for (size_t i = 0; i < n; i++) in[i] = (cl_uint)(i * 2654435761u);
cl_mem bin = clCreateBuffer(ctx, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, n * 4, in, &err); CHECK(err);
cl_mem bout = clCreateBuffer(ctx, CL_MEM_READ_WRITE | CL_MEM_COPY_HOST_PTR, n * 4, out, &err); CHECK(err);
cl_uint s = 3;
CHECK(clSetKernelArg(k, 0, sizeof bout, &bout));
CHECK(clSetKernelArg(k, 1, sizeof bin, &bin));
CHECK(clSetKernelArg(k, 2, sizeof s, &s));
size_t gsz = G, lsz = 32;
CHECK(clEnqueueNDRangeKernel(q, k, 1, NULL, &gsz, &lsz, 0, NULL, NULL));
CHECK(clEnqueueReadBuffer(q, bout, CL_TRUE, 0, n * 4, out, 0, NULL, NULL));
// out[7 - v] should be in[v] + p[v * s % P] = in[v] + v
size_t bad = 0;
for (size_t v = 0; v < V; v++)
for (size_t g = 0; g < G; g++)
for (size_t c = 0; c < W; c++) {
cl_uint got = out[((V - 1 - v) * G + g) * W + c];
cl_uint want = in[(v * G + g) * W + c] + (cl_uint)v;
if (got != want && bad++ < 5)
printf("mismatch: vector %zu, item %zu, lane %zu: got 0x%08x, want 0x%08x\n",
v, g, c, got, want);
}
printf("%s: %zu of %zu values wrong\n", bad ? "FAIL" : "PASS", bad, n);
return bad != 0;
}
- Lingua principale
- C++
- Stelle
- 725
- Fork
- 200
- Metriche di merge delle PR
- Nessuna PR unita negli ultimi 30g
Preparare l'ambiente
- Nessun Dockerfile né file Docker Compose
- Nessun modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di intel/intel-graphics-compiler
-
functionControl internal option is documented but only the misspelled functonControl worksForse di nuovo libera Una pull request per questa issue è stata chiusa senza essere unita. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100
intel/intel-graphics-compiler#422 · 2 commenti ·
-
add_strerror_r_to_error missing int overloadForse già presa @cdecker08 l’ha presa 425 giorni fa. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
intel/intel-graphics-compiler#373 ·
-
CMake doesn't handle the case when multiple `opencl-c.h` are foundForse già presa @pvelesko l’ha presa 75 giorni fa. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
intel/intel-graphics-compiler#314 · 1 commento ·
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 49/100
intel/intel-graphics-compiler#475 ·
-
Gen12LP: OWord spills of indirectly accessed variables use the wrong slotForse già presa @maleadt l’ha presa 4 giorni fa. Aperta
Difficoltà 4/5 3-5 giorni Idoneità per principianti 53/100
intel/intel-graphics-compiler#472 ·
Tutte le issue di intel/intel-graphics-compiler
Issue simili
-
enhancement
Difficoltà 2/5 1-3 ore Idoneità per principianti 79/100
-
area: ecs bug priority: P2 type: testing
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
ponkoengine/ponko#321 ·
I maintainer di solito rispondono entro 1 giorno
-
runtime: lf_f128_mod is documented as fmodq (truncating) but implements floor-based modulo; unusedApertainternal correctness low priority rt_lib
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 87/100
-
PriorityNotificationQueue::wakeUpAll() loses the wake-up for a thread that is not waiting yetForse già presa @matejk l’ha presa oggi. Apertabug
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
pocoproject/poco#5674 ·
I maintainer di solito rispondono entro 2 giorni