Description
I think this is something that needs to be worked around in the translator properly.
Starting with llvm/llvm-project@7a086e1 clang zero initializes in C dialects besides C++ all padding in static initializers, which do include Program scope variables in the global address space.
Assume code like this (OpenCL CTS test_basic progvar_prog_scope_misc
):
input1.cl:
typedef struct { uchar c; uint i; } my_struct_t;
my_struct_t var = { 'a', 42 };
kernel void writer( uchar c, uint i ) {
var.c = c;
var.i = i;
}
and input2.cl:
typedef struct { uchar c; uint i; } my_struct_t;
extern my_struct_t var;
kernel void reader( global uchar* C, global uint* I ) {
*C = var.c;
*I = var.i;
}
The types of the global variable var
ends up being different in the translation units:
@var = addrspace(1) global { i8, [3 x i8], i32 } { i8 97, [3 x i8] zeroinitializer, i32 42 }, align 4
vs
%struct.my_struct_t = type { i8, i32 }
@var = external addrspace(1) global %struct.my_struct_t, align 4
This also leads to different types for those variables on the SPIR-V level and therefore makes spirv-link fail with this: Type mismatch on symbol "var" between imported variable/function %47 and exported variable/function %13.
One could convince spirv-link
to link variables together despite their different type, however this will cause access chains to point to the wrong members in some translation units.
I don't really know on which level this could be handled the best. I see a couple of options:
- make clang not do it for OpenCL C
- make LLVM add the padding fields also in kernels without initializers
- make the translator treat imported global variables as opaque (give it some random type, like being a single byte) and cast pointers to that variable on use
- add some lowering to spirv-link to pick one type and fix up all derefs (can be difficult, because how would the linker know what's padding and what's real).