<div dir="ltr"><div dir="ltr">Hi all,<br><br>Reviving this thread as Johannes and I recently had some time to take a look and do some additional design work. We'd love any thoughts on the following proposal.<div><br></div><div><i>Background:</i></div><div><br></div><div>Standard math (and potentially other) functions for both AMD and NVIDIA GPU's don't exist and LLVM-based tools must instead call architecture-specific functions that perform similar computations.<br><br>For example in clang/lib/Headers/__clang_hip_math.h:</div><div><br>__DEVICE__<br>double sqrt(double __x) { return __ocml_sqrt_f64(__x); }<br></div><div><br></div><div>and clang/lib/Headers/__clang_cuda_math.h:</div><div><br></div><div>__DEVICE__ double sqrt(double __a) { return __nv_sqrt(__a); }<br></div><div><br>In the case of CUDA, the definition of these functions are found by immediately linking against a corresponding CUDA libdevice.bc<br><br></div><div>This design presents several problems:</div><div><br></div><div>1) It is illegal to use llvm math intrinsics on GPU code as these functions do not have definitions.</div><div><br></div><div>While in theory we could define the lowering of these intrinsics to be a table which looks up the correct __nv_sqrt, this would require the definition of all such functions to remain or otherwise be available. As it's undesirable for the LLVM backend to be aware of CUDA paths, etc, this means that the original definitions brought in by merging libdevice.bc must be maintained. Currently these are deleted if they are unused (as libdevice has them marked as internal).<br><br>2) GPU math functions aren't able to be optimized, unlike standard math functions.</div><div><br></div><div>Since LLVM has no idea what these foreign functions are, they cannot be optimized. This is problematic in two ways. First, these functions to not have all the relevant attributes one might expect (inaccessiblememonly, willreturn, etc). Secondly, they cannot benefit from instcombine-style optimizations that recognize math intrinsic. For example, a call to sin(0) from source code will remain a call to __ocml_sqrt_f32(0) [if on AMD] rather than being replaced with 0.</div><div><br></div><div>These two design issues make it difficult for tools that wish to generate GPU code (frontends, target offloading, Enzyme AD tool, etc) as well as simply being able to optimize it effectively.</div><div><br></div><div><i>Design Constraints:</i></div><div><i><br></i></div><div>To remedy the problems described above we need a design that meets the following:</div><div>* Does not require modifying libdevice.bc or other code shipped by a vendor-specific installation</div><div>* Allows llvm math intrinsics to be lowered to device-specific code</div><div>* Keeps definitions of code used to implement intrinsics until after all potential relevant intrinsics (including those created by LLVM passes) have been lowered.</div><div><div><br></div><div><i>Initial Design:</i></div></div><div><br></div><div>To remedy this we propose a refined version of the implements mechanism described above. Specifically, consider the example below:</div><div><br></div><div>define internal float @my_cos_fast(float %d) {<br>  ...<br>}<br><br>declare internal float @my_cos(float %d)<br><br>define double @foo(double %d, float %f) {<br>  %c1 = tail call fast double @llvm.cos.f64(double %d)<br>  %c2 = tail call fast double @cos(double %d)<br>  ret double %c2<br>}<br><br>declare double @cos(double) !metadata !1 <br>declare double @llvm.cos.f64(double) !metadata !0 <br><br>!0 = !{!"implemented_by", double(double)* @my_cos}<br>!1 = !{!"implemented_by", double(double)* @my_cos_fast}<br></div><div><br></div><div>Here, each function that we may want to provide an implementation for (in this case cos and llvm.cos.f64), has a metadata tag "implemented_by" followed by the function which it will be replaced with. The type signature of the original function and its implementation must match.<br><br>The implemented_by metadata will be defined to ensure both the replacement and the replacee will be kept around (for example to ensure that LLVM passes that generate a call to llvm.cos will still have a definition).<br><br>After all passes that could generate such intrinsics and instruction simplifications have run, a new LLVM optimization pass that replaces uses of the function with its implementation.</div><div><br></div><div>Proposed Patches:</div><div><br></div><div>





<p style="margin:0px;font-variant-numeric:normal;font-variant-east-asian:normal;font-stretch:normal;font-size:13px;line-height:normal;font-family:"Helvetica Neue"">1) Allow metadata on declaration [not just definition]</p>
<p style="margin:0px;font-variant-numeric:normal;font-variant-east-asian:normal;font-stretch:normal;font-size:13px;line-height:normal;font-family:"Helvetica Neue"">2) Tell GlobalOpt and other passes not to delete globals using/used in implemented_by</p>
<p style="margin:0px;font-variant-numeric:normal;font-variant-east-asian:normal;font-stretch:normal;font-size:13px;line-height:normal;font-family:"Helvetica Neue"">3) Write implementedby pass that scans all functions, replaces call, removes metadata<br></p>
<p style="margin:0px;font-variant-numeric:normal;font-variant-east-asian:normal;font-stretch:normal;font-size:13px;line-height:normal;font-family:"Helvetica Neue"">4) Add Clang  attributes to expose implements and use in nvptx/amd<span> headers</span></p></div><div><br>Cheers,<br>Billy</div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Fri, Mar 12, 2021 at 6:00 PM Artem Belevich via llvm-dev <<a href="mailto:llvm-dev@lists.llvm.org" target="_blank">llvm-dev@lists.llvm.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div dir="ltr"><div style="font-family:verdana,sans-serif"><br></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Fri, Mar 12, 2021 at 2:39 PM James Y Knight <<a href="mailto:jyknight@google.com" target="_blank">jyknight@google.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div dir="ltr"><br></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Fri, Mar 12, 2021 at 1:51 PM Artem Belevich via llvm-dev <<a href="mailto:llvm-dev@lists.llvm.org" target="_blank">llvm-dev@lists.llvm.org</a>> wrote:</div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Also, bitcode is<span class="gmail_default" style="font-family:verdana,sans-serif"> </span>platform specific. I can imagine building a bitcode file during the<br>
build but shipping one means you have to know ABI and datalayout or<br>
hope they are the same everywhere.<br></blockquote><div><br></div><div><div style="font-family:verdana,sans-serif">Agreed. We will likely need multiple variants. We will compile specifically for NVPTX or AMDGPU and we will know specific ABI and the data layout for them regardless of the host we're building on.</div></div><div><br></div><div><div style="font-family:verdana,sans-serif">It appears to me is the the difference vs what we have now is that we'll need to have the libm sources somewhere, the process to build them for particular GPUs (that may need to be done out of the tree as it may need CUDA/HIP SDKs) and having to incorporate such libraries into llvm distribution.</div><br></div><div><div style="font-family:verdana,sans-serif">OK. I'll agree that that may be a bit too much for now.</div></div></div></div></blockquote><div><br></div><div>It sounded before like you were saying the library should effectively be function aliases for standard libm names, to call __nv_ names. Isn't it utterly trivial to generate such a bitcode file as part of the toolchain build, without requiring any external SDKs?</div></div></div></blockquote><div><br></div><div><div style="font-family:verdana,sans-serif">That's true for most, but not all functions provided by libdevice. We'd still need something that's a bit more involved.</div><br></div><div><div style="font-family:verdana,sans-serif">--Artem</div><br></div><div><br></div><div> </div></div><br clear="all"><div><br></div>-- <br><div dir="ltr"><div dir="ltr">--Artem Belevich</div></div></div>
_______________________________________________<br>
LLVM Developers mailing list<br>
<a href="mailto:llvm-dev@lists.llvm.org" target="_blank">llvm-dev@lists.llvm.org</a><br>
<a href="https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev" rel="noreferrer" target="_blank">https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev</a><br>
</blockquote></div></div>