-
Notifications
You must be signed in to change notification settings - Fork 14k
Offload intrinsic #147936
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
base: main
Are you sure you want to change the base?
Offload intrinsic #147936
Conversation
9118683 to
23722aa
Compare
This comment has been minimized.
This comment has been minimized.
| } | ||
|
|
||
| pub fn from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Self { | ||
| OffloadMetadata { payload_size: get_payload_size(tcx, ty), mode: TransferKind::Both } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you already have the code here, I would add a small check for & or byVal (implies Mode ToGPU), vs &mut (implies Both).
In the future we would hope to analyze the & or byval case more, if we never read from it (before writing) then we could use a new mode 4, which allocates directly on the gpu.
This comment has been minimized.
This comment has been minimized.
|
☔ The latest upstream changes (presumably #148507) made this pull request unmergeable. Please resolve the merge conflicts. |
e0fd7be to
97a8e96
Compare
This comment has been minimized.
This comment has been minimized.
|
☔ The latest upstream changes (presumably #148721) made this pull request unmergeable. Please resolve the merge conflicts. |
| let i32_0 = cx.get_const_i32(0); | ||
| for index in 0..types.len() { | ||
| let v = unsafe { llvm::LLVMGetOperand(kernel_call, index as u32).unwrap() }; | ||
| for index in 0..num_args { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you iterate directly over args now?
| return Ok(()); | ||
| } | ||
| sym::offload => { | ||
| // FIXME(Sa4dUs): emit error when offload is not enabled |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather have that as a TODO, thus fixed before we land it. You can just copy the check I had above (and potentially error if you find an intrinsic, but not the flag set). The current main already requires it, and we shouldn't change nightly behaviour because of an experimental feature without a user setting a feature flag.
| | ty::FnDef(_, _) | ||
| | ty::FnPtr(_, _) | ||
| | ty::Closure(_, _) | ||
| | ty::CoroutineClosure(_, _) | ||
| | ty::Coroutine(_, _) | ||
| | ty::CoroutineWitness(_, _) | ||
| | ty::Never |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd expect all of these to not be handled correctly by offload without further work, so I'd just error.
The same goes for
| ty::Bound(_, _)
| ty::Placeholder(_)
| ty::Infer(_)
| ty::Error(_)
3540edb to
e9d89ce
Compare
This comment has been minimized.
This comment has been minimized.
e9d89ce to
a08949b
Compare
This comment has been minimized.
This comment has been minimized.
a08949b to
9397d31
Compare
This comment has been minimized.
This comment has been minimized.
9397d31 to
7666b58
Compare
This comment has been minimized.
This comment has been minimized.
e38248d to
b34be91
Compare
| #[rustc_intrinsic] | ||
| pub const fn autodiff<F, G, T: crate::marker::Tuple, R>(f: F, df: G, args: T) -> R; | ||
|
|
||
| /// Generates the LLVM body of a wrapper function to offload a kernel `f`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have other backends besides LLVM, so intrinsics typically should be described in terms of what they do, not implementation details. Is that possible here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this intrinsic only makes sense in LLVM right now because it relies directly on LLVM's offload feature. that's why i wanted to specify the backend
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if there's a better way to proceed, please let me know
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, seems like we did something similar for the autodiff intrinsic. I would assume that the concept of offloading is independent of LLVM, but maybe we don't have to figure out that full story at this point.
Are there docs for the LLVM offload feature you could link to?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i'd say https://clang.llvm.org/docs/OffloadingDesign.html contains all the relevant details
ping @ZuseZ4 in case he has something better
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, your link is probably the best overview. Offload grew out of OpenMP, which is also supported by other compilers like GCC. LLVM just put in some effort to split Offloading and OpenMP, so that the former is easier to use independently. https://gcc.gnu.org/projects/gomp/
ping @antoyo just for awareness.
With respect to a high-level explanation of this intrinsic:
We use a single-source, two-pass compilation approach. We compile all functions that should be offloaded for the device (e.g nvptx64, amdgcn-amd-amdhsa, intel in the future) and which are marked by our intrinsic. We then compile the code for the host (e.g. x86-64), where most of the offloading logic happens. On the host side, we generate calls to the openmp offload runtime, to inform it about the layout of the types (a simplified version of the autodiff TypeTrees). We also use the type system to figure out whether kernel arguments have to be moved only to the device (e.g. &[f32;1024]), from the device, or both (e.g. &mut [f64]). We then launched the kernel, after which we inform the runtime to end this environment and move data back (as far as needed).
There are obviously a lot of features and optimizations which we want to add in the future. The Rust frontend currently also mostly uses the OpenMP API, since it was more stable back when I started working on it. We intend to move over to the newer offload API, which is slightly lower level.
This PR implements the minimal mechanisms required to run a small subset of arbitrary offload kernels without relying on hardcoded names or metadata.
offload(kernel, (..args)): an intrinsic that generates the necessary host-side LLVM-IR code.rustc_offload_kernel: a builtin attribute that marks device kernels to be handled appropriately.Example usage (pseudocode):