-
Notifications
You must be signed in to change notification settings - Fork 223
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How to deal with opaque pointers in the translator #1444
Comments
Or actually, we can just emit i8* in SPIR-V. |
|
Possible problem with the proposed design (and opaque pointers in general): would |
I suggest that we proceed with phase 0 before deciding on the next steps. The reason for that is that generating magic opaque structures is an extra work we will have to do and presence of such structures could confuse backends consuming SPIR-V: not that functionality will be affected, but performance might be impacting due to analysis passes incorrectly handle extra opaque structures. Hopefully, we will be able to deduce most of pointer types from neighbor instructions as suggested in migration instructions and generate SPIR-V which is the same as we generate now without opaque pointers in input. |
|
I've got some local patches that start with the transition to opaque pointers. The first of these is already a PR (#1463), and I'm holding off on posting the others more because Github works poorly with patches-that-depend-on-others. For now, I've focused on part 0, getting rid of The first set of changes is the "trivial" set of changes--the cases where you have to look slightly further afield to get the type of pointer (for example, The second set of changes is getting This leaves two residual sets of uses of The more worrying set of uses is the queries to the struct name of the type returned by |
|
@alan-baker You might find this discussion interesting. |
Agreed; I think it'll be least disruptive for consumers of translator output if we aim to generate the same SPIR-V with and without opaque pointers. That will require some nontrivial effort on the translator side, but I think it's the best way forward.
When you say "output SPIR-V" did you mean to say "output LLVM IR"? This is a valid concern, though the type names might also be available in the mangled name in most cases. That will require updating device compilers, but it might be inevitable when LLVM drops support for nonopaque pointers? |
…#1444). This is the easiest tranche of changes: where the pointer element type is usually retrieved by looking a little further afield for the element type, or via scavenging from sret/byval parameters.
…#1444). This is the easiest tranche of changes: where the pointer element type is usually retrieved by looking a little further afield for the element type, or via scavenging from sret/byval parameters.
…#1444). This is the easiest tranche of changes: where the pointer element type is usually retrieved by looking a little further afield for the element type, or via scavenging from sret/byval parameters.
…#1444). This is the easiest tranche of changes: where the pointer element type is usually retrieved by looking a little further afield for the element type, or via scavenging from sret/byval parameters.
…#1444). This is the easiest tranche of changes: where the pointer element type is usually retrieved by looking a little further afield for the element type, or via scavenging from sret/byval parameters.
…#1444). This is the easiest tranche of changes: where the pointer element type is usually retrieved by looking a little further afield for the element type, or via scavenging from sret/byval parameters.
…#1444). This is the easiest tranche of changes: where the pointer element type is usually retrieved by looking a little further afield for the element type, or via scavenging from sret/byval parameters.
This is the easiest tranche of changes: where the pointer element type is usually retrieved by looking a little further afield for the element type, or via scavenging from sret/byval parameters.
|
I figured I'd give a not-so-brief status update of where I am in opaque pointer translation. In my local workspace, I'm doing to 18 calls to
Absent these patches, there are still several calls to
|
I like it!
Probably the following test addresses this code: https://github.com/KhronosGroup/SPIRV-LLVM-Translator/blob/master/test/transcoding/global_block.cl
It actually does the following:
I guess, we can go to https://github.com/intel/llvm/blob/sycl/sycl/include/CL/__spirv/spirv_types.hpp#L135 and remove the pointer, though it will require some code adjustments and actually doing some initialization of the structure, which is a bit undesired. But from the top of my head I don't see a better way. Side note for it, https://github.com/KhronosGroup/SPIRV-LLVM-Translator/blob/master/lib/SPIRV/SPIRVRegularizeLLVM.cpp#L459 is a wrong describtion, recently there was a 'Use' parameter added to the Matrix type layout, hence the comment should be updated accordingly. |
It doesn't actually reach that method--I've dropped an This pass appears to do nothing if there are no calls to So I think this code is dead and has been dead for several years. Indeed, if I disable the pass entirely and run the test suite, all of the tests pass. I'd like to propose removing this code entirely if it's truly dead, but I'm not savvy enough about how this code is used to say for certain. Outside of that case, my local workspace is down to 4 calls to I've got two PRs open right now to fix up, and probably another two or three PRs that aren't going to be rolled up into the big PRs. |
|
Now that LLVM turned on opaque pointers by default ( llvm/llvm-project@702d5de ), I first reported it on LLVM side: but it looks like that's Here is the kind of backtrace I get when building latest llvm-spirv: include/llvm/IR/Type.h:384: llvm::Type *llvm::Type::getNonOpaquePointerElementType() const: Assertion `NumContainedTys && "Attempting to get element type of opaque pointer"' failed.
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace.
Stack dump:
0. Program arguments: bin/llvm-spirv --spirv-max-version=1.1 -o spirv64-mesa3d-.spv builtins.link.spirv64-mesa3d-.bc
1. Running pass 'Adapt OCL types for SPIR-V' on module 'builtins.link.spirv64-mesa3d-.bc'.
Build libclc: [ 99%] Built target opt.spirv-mesa3d-.bc
Build libclc: [ 99%] Built target prepare-clspv--.bc
Build libclc: [ 99%] Built target prepare-clspv64--.bc
#0 0x00007f74b67c97e2 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) llvm-project/llvm/lib/Support/Unix/Signals.inc:565:22
#1 0x00007f74b67c98a9 PrintStackTraceSignalHandler(void*) llvm-project/llvm/lib/Support/Unix/Signals.inc:632:1
#2 0x00007f74b67c7454 llvm::sys::RunSignalHandlers() llvm-project/llvm/lib/Support/Signals.cpp:103:20
#3 0x00007f74b67c9113 SignalHandler(int) llvm-project/llvm/lib/Support/Unix/Signals.inc:407:1
#4 0x00007f74b5011520 (/lib/x86_64-linux-gnu/libc.so.6+0x42520)
#5 0x00007f74b5065828 __pthread_kill_implementation ./nptl/pthread_kill.c:44:76
#6 0x00007f74b5065828 __pthread_kill_internal ./nptl/pthread_kill.c:80:10
#7 0x00007f74b5065828 pthread_kill ./nptl/pthread_kill.c:91:10
#8 0x00007f74b5011476 gsignal ./signal/../sysdeps/posix/raise.c:27:6
#9 0x00007f74b4ff77b7 abort ./stdlib/abort.c:81:7
#10 0x00007f74b4ff76db get_sysdep_segment_value ./intl/loadmsgcat.c:509:8
#11 0x00007f74b4ff76db _nl_load_domain ./intl/loadmsgcat.c:970:34
#12 0x00007f74b5008e26 (/lib/x86_64-linux-gnu/libc.so.6+0x39e26)
#13 0x000055f6364f4e97 llvm::Type::getNonOpaquePointerElementType() const (bin/llvm-spirv+0x10be97)
#14 0x000055f636594c65 llvm::Type::getPointerElementType() const (bin/llvm-spirv+0x1abc65)
#15 0x000055f6365b91bd SPIRV::isPointerToOpaqueStructType(llvm::Type*) (bin/llvm-spirv+0x1d01bd)
#16 0x000055f63675e93d SPIRV::OCLTypeToSPIRVBase::adaptFunctionArguments(llvm::Function*) (bin/llvm-spirv+0x37593d)
#17 0x000055f63675e3c3 SPIRV::OCLTypeToSPIRVBase::runOCLTypeToSPIRV(llvm::Module&) (bin/llvm-spirv+0x3753c3)
#18 0x000055f63675e201 SPIRV::OCLTypeToSPIRVLegacy::runOnModule(llvm::Module&) (bin/llvm-spirv+0x375201)
#19 0x00007f74b6abc94d (anonymous namespace)::MPPassManager::runOnModule(llvm::Module&) llvm-project/llvm/lib/IR/LegacyPassManager.cpp:1545:20
#20 0x00007f74b6ab767a llvm::legacy::PassManagerImpl::run(llvm::Module&) llvm-project/llvm/lib/IR/LegacyPassManager.cpp:535:13
#21 0x00007f74b6abd25f llvm::legacy::PassManager::run(llvm::Module&) llvm-project/llvm/lib/IR/LegacyPassManager.cpp:1673:1
#22 0x000055f63660aba2 llvm::writeSpirv(llvm::Module*, SPIRV::TranslatorOpts const&, std::ostream&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&) (bin/llvm-spirv+0x221ba2)
#23 0x000055f6364c1db5 convertLLVMToSPIRV(SPIRV::TranslatorOpts const&) llvm-spirv.cpp:0:0
#24 0x000055f6364bf942 main (bin/llvm-spirv+0xd6942)
#25 0x00007f74b4ff8fd0 __libc_start_call_main ./csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#26 0x00007f74b4ff907d call_init ./csu/../csu/libc-start.c:128:20
#27 0x00007f74b4ff907d __libc_start_main ./csu/../csu/libc-start.c:379:5
#28 0x000055f6364bc5e5 _start (bin/llvm-spirv+0xd35e5) |
|
Note: in contradiction to the documentation, building clang with |
Have you considered a change to clang to set the |
Setting |
|
I just looked the verifier and you're right, but I guess that raises the question should there be an OpenCL attribute basically like element type for kernel arguments? |
|
Clang already creates metadata for OpenCL kernel argument types that contains the (source-level) type information for each parameter. |
|
There are such kernel_arg metadata, though I'm not sure, wether they are 'standart'. Currently the translator hadles them, transforming into OpString instruction and translates this string backwards only if the appropriate option is passed among -r option, see #1109 . |
This metadata is emitted by OpenCL front-end only and it's optional. I think it's not emitted by DPC++ compiler. I don't quite get what is the plan for the code example like this (https://godbolt.org/z/WzYYhTYe3): ; Function Attrs: convergent norecurse nounwind
define dso_local spir_func void @sample_func_read(ptr addrspace(1) noundef %0, ptr addrspace(1) %1, ptr addrspace(2) %2, <2 x float> noundef %3, <2 x float> noundef %4, <2 x float> noundef %5) local_unnamed_addr #0 {
tail call spir_func void @my_lib_func(ptr addrspace(1) noundef %0, ptr addrspace(1) %1, ptr addrspace(2) %2, <2 x float> noundef %3, <2 x float> noundef %4, <2 x float> noundef %5) #2
ret void
}
; Function Attrs: convergent
declare dso_local spir_func void @my_lib_func(ptr addrspace(1) noundef, ptr addrspace(1), ptr addrspace(2), <2 x float> noundef, <2 x float> noundef, <2 x float> noundef) local_unnamed_addr #1This example doesn't use neither OpenCL built-in function calls nor kernel function definitions which might be used to recover SPIR-V type information from LLVM IR code. One of the goals of SPIR-V format states following: "Map easily to other intermediate languages". I think LLVM IR was mentioned there as one of the intermediate languages at some point. Today OpenCL front-end represents all OpenCL/SPIR-V specific types (e.g. sampler, image, pipe, etc.) as type-less pointer, so it doesn't seem to be easy to map LLVM IR to SPIR-V anymore. :( |
My current approach is to back everything off to
I don't have much personal insight into the needs of the ultimate OpenCL backends with respect to these opaque types. However, the general trend in LLVM optimization passes is that retaining type information is usually more work than it is worth (beyond opaque pointer transition, there is a mild push towards converting geps to i8 offsets instead of retaining type information there). If these types only need to be distinguished at call sites to builtins, then something along the lines of #1477 is sufficient. If reasoning needs to happen more distantly from those call sites, then it is probably advisable to change their representation in LLVM: things are only going to get worse.
At my current stage of SPIRVWriter work, I am running into some issues getting these types to validate properly in cases like PHI nodes or select statements. Nothing so far that isn't fixable, but I'm not finding an easy solution where I can be satisfied that the translation will be correct. |
SPIR-V bitcast doesn't allow casting pointers to some types (e.g. OpTypeSampler) and I don't see any other standard instructions allowing such conversion. If I understand @MrSidims proposal in the issue description correctly, he suggests legalizing casts between type-less pointers and SPIR-V types with new extension to the spec. Am I right? Has anyone started working on the extension proposal? @iliya-diyachkov, @michalpaszkowski, do you have a plan for dealing with SPIR-V types in SPIR-V back-end? |
|
@bader, There is an ongoing effort to update SPIR-V extensions and accommodate untyped pointers. Here is a link to the merge request from @alan-baker: https://gitlab.khronos.org/spirv/spirv-extensions/-/merge_requests/202. Once the proposal is approved, I think we can start working on the implementation? |
|
@bader no special plan yet. Currently we are just following the direction in which the translator goes. |
|
@asudarsa, thanks for the pointer. I looked through the document and it solves only part of the problem. This extension helps with translation types which are represented as a typed pointer in SPIR-V. The case I'm concerned about is SPIR-V types, which are not pointers, but still represented as a typed pointers in LLVM IR (e.g. OpTypeSampler is a pointer to opaque structure type with fixed name). SPIR-V translator is not able to recover a SPIR-V type if pointer type information is not present. The code snippet from this comment shows an example of LLVM IR with no type information in metadata or mangled function name.
@iliya-diyachkov, does it mean clang is going to emit additional type information we can use to recover SPIR-V types like samplers/images/etc. for non-kernel functions as well? |
@bader I have not discussed it with clang guys, but I think it can be implemented this way. |
|
@bader After talking to clang guys I suppose it's easy to make a patch which also hints arguments of non-kernel functions like this (for your example): Note that the declaration of my_lib_func is not annotated but it's also can be implementing (I think it will not require much work). What is your opinion if the function args is the only place where we strongly need pointer annotation at the moment? |
I'm not sure if annotating function parameters is enough to recover SPIR-V types correctly in all cases, but I hope so. The example I provided came to my mind after reading how types are suggested to be recovered at the moment. I have a few questions regarding this proposal:
|
|
Some thoughts from my experiences trying to implement the type scavenger: Firstly, a bidirectional approach is rather more difficult to implement. It is really better to be able to have the pointee type known at the value source, and then push through the code and insert bitcasts when the use implies a pointee type different from the source type. With this in mind, I can generally categorize pointer-typed values into three categories. They are as follows (keep in mind that the rules for constant expressions are the same as their corresponding instructions):
There's one thing I haven't included in this list, and that's I will also note that, where LLVM tells us the pointee type of the value, we get at best a single level of pointer indirection. If an From the perspective of my implementation attempts, having known-correct types at the function boundary makes implementation simpler. a single reverse-postorder traversal of every function would type it sufficiently well, and even reverse-postorder may not be necessary. In essence, if the third category doesn't exist (or you can at least get something working by treating A rather more important issue, though, is the need to be able to positively identify what pointee type a given use of a pointer expects it to be. Modulo pointers-to-pointers issues, the only instructions that don't immediately have identifiable pointee type uses are Answering the questions:
Instruction traversal is required anyways, to insert bitcasts where needed. To me, the question isn't can you avoid instruction traversal, but rather, can you avoid visiting each instruction more than once.
The design of metadata is supposed to be that it's correct to drop all metadata, and passes should drop all metadata they don't understand. The latter requirement tends to be frequently flouted by optimization passes, but metadata on functions is even less likely to be disturbed since you're never considering that you might be changing the semantics of a function.
At some point, I believe, there is a need for frontends to change their IR to cope with LLVM's opaque pointers. Adding function metadata is one of way doing it, but it might not be the best way; another idea I'd suggest (assuming that the SPIR-V opaque types are the main issue here) is to make each type a pointer in a different address space. |
|
Thanks for sharing your experiences @jcranmer-intel. Just wanted to highlight one point you're touching on:
I've been thinking about this too, but haven't had the time to play with this yet. It does make sense conceptually, since one opaque type generally can't be casted to another, and I think this could be a more robust solution than metadata. |
|
A status update: I've uploaded #1499, which hits a very important milestone: with that PR, it is possible to compile the most basic of basic kernels in opaque pointer mode (I flipped the simplest test I could find to use opaque pointers to prove so). I'm not yet certain enough of the type scavenger's structure to fold that into a patch for review just yet, but the current pass rate I have with my local version when I compiled the SYCL test suite with opaque pointers is 90%. I don't have any numbers for OpenCL test suite pass rates, because those tend to filter through Something I'll try in the near future is to make the test suite check SPIR-V-to-LLVM conversion using opaque pointers, which should work already. |
|
By the way I have submitted the draft patch which I mentioned before (it hints arguments of non-kernel functions) so you can build and try patched clang. |
|
Another status update: I have just uploaded #1512, which implements type scavenging well enough to mass-revert the |
|
If you're not aware yet, I've posted on RFC on discourse about opaque pointers and SPIR-V here: https://discourse.llvm.org/t/rfc-better-support-for-typed-pointers-in-an-opaque-pointer-world/63339 |
|
Status update time again: with the landing of #1512, we are down to two calls to My transition plan for The biggest wrinkle in this plan is that a few of the builtins take an array of opaque types, which are currently represented as something like
|
…#1444). This is the easiest tranche of changes: where the pointer element type is usually retrieved by looking a little further afield for the element type, or via scavenging from sret/byval parameters.
|
Status update, since I've realized I haven't done this for a while: intel/llvm#6535 is the frontend change that removes the need for the last remaining call to The final major piece for opaque pointer enabling is fixing the calls to In parallel to that effort, what still remains to be done is porting tests to use opaque pointers instead of typed pointers, as well as fixing regressions that happen when you turn on opaque pointers--there are likely to be quite a few of them! Based on some of my previous test runs in opaque pointer mode, we are likely to be better served by introducing an LLVM opaque type (as proposed in the link at #1444 (comment)), but I haven't worked out a transition plan for that yet. To give that famous quote, this is not the end. It is not even the beginning of the end. But it is, perhaps, the end of the beginning. |
|
Any update on this? |
|
As of a few weeks ago (sorry for not updating this issue sooner), the code no longer calls Right now, my focus is on getting opaque types added into LLVM (https://reviews.llvm.org/D135202), which is necessary to fix some of the issues with using the special SPIR-V image and sampler types. Beyond that, there are still a handful of lit tests that fail to work with |
|
LLVM have clarified the timeline for typed pointer support: https://reviews.llvm.org/D140487
|
|
typed pointer support is going away in a week, any update now that https://reviews.llvm.org/D135202 landed? |
|
@aeubanks there is also an important clang PR https://reviews.llvm.org/D141008 that should be merged for OpenCL types support. And it has the appropriate counterpart here in the translator #1799 |
|
Once the things that @MrSidims refers to have landed, I will consider opaque pointer support conceptually complete, but there are still some bugs in opaque pointer support that haven't been fully fixed. |
|
fyi, I've opened this PR recently which adds So with this, spirvs can be linked regardless of the specific pointer types in imported function signatures. |
|
FYI, SPV_KHR_untyped_pointers was provisionally released which would allow a more direct translation if supported by the implementation. |
|
AFAIK @vmaksimo is working on its implementation |
|
Closing this issue, will open one for SPV_KHR_untyped_pointers separately. |
for reference: https://llvm.org/docs/OpaquePointers.html#migration-instructions
We have discussed some high-level plan about what to do in the translator. This issue is reflecting this plan leaving the field to discuss the details.
LLVM community is moving towards typeless pointers. There are several things to do in the translator to make sure typeless pointers are translatable:
0. Get rid of LLVM pointer-element-type dependent API in the translator code base, for example
getElementTypeitself;Represent typeless pointers somehow. Currently we don't have any extension for this and even if we had - still we would need to translate such LLVM IR to SPIR-V and backwards. As an option we can create pointers to a magically named opaque structure in SPIR-V. Note, that it would require to add extra casts to/from this new type for the pointer users.
During reverse translation there is no good way to differ this opaque structure pointer from the true typeless pointer. So to make sure, that new typesless pointer is being emitted by SPIR-V consumer we probably need to introduce an option that forces any pointer type to become opaque. This option is also enforcing the translator to eliminate casts that were inserted on the previous step (or that were presenting in the LLVM IR before). If the option is not passed during the reverse translation (default behavior at least for the first time), the translator should emit LLVM IR as there are no typeless pointers at all (aka emit pointers to opaque structure).
We actually need to have opaque/typeless pointer extension and make it properly working with the mentioned SPIR-V consumer option.
At minimum we would need to translate the following IR snippet, I assume the translation flow (without an extension) should be looking like:
LLVM IR before the translator:
Generated SPIR-V:
LLVM IR after the translator (with --force-opaque-pointers option):
LLVM IR after the translator (without --force-opaque-pointers option):
The text was updated successfully, but these errors were encountered: