1use std::ffi::CString;
2
3use bitflags::Flags;
4use llvm::Linkage::*;
5use rustc_abi::{Align, HasDataLayout, Integer, Primitive, Size};
6use rustc_codegen_ssa::MemFlags;
7use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue};
8use rustc_codegen_ssa::mir::place::PlaceValue;
9use rustc_codegen_ssa::traits::{
10 BaseTypeCodegenMethods, BuilderMethods, ConstCodegenMethods, ReturnSlot,
11};
12use rustc_middle::ty::offload_meta::{MappingFlags, OffloadMetadata, OffloadSize};
13use rustc_span::bug;
14
15use crate::attributes;
16use crate::builder::Builder;
17use crate::common::CodegenCx;
18use crate::context::SimpleCx;
19use crate::llvm::AttributePlace::Function;
20use crate::llvm::{self, Linkage, Type, TypeKind, Value};
21
22pub(crate) struct OffloadGlobals<'ll> {
24 pub launcher_fn: llvm::Value<'ll>,
25 pub launcher_ty: llvm::Type<'ll>,
26
27 pub kernel_args_ty: KernelArgsTy,
28
29 pub offload_entry_ty: llvm::Type<'ll>,
30
31 pub begin_mapper: llvm::Value<'ll>,
32 pub end_mapper: llvm::Value<'ll>,
33 pub mapper_fn_ty: llvm::Type<'ll>,
34
35 pub ident_t_global: llvm::Value<'ll>,
36}
37
38impl<'ll> OffloadGlobals<'ll> {
39 pub(crate) fn declare(cx: &CodegenCx<'ll, '_>) -> Self {
40 let (launcher_fn, launcher_ty) = generate_launcher(cx);
41 let kernel_args_ty = KernelArgsTy::new_decl(cx);
42 let offload_entry_ty = TgtOffloadEntry::new_decl(cx);
43 let (begin_mapper, _, end_mapper, mapper_fn_ty) = gen_tgt_data_mappers(cx);
44 let ident_t_global = generate_at_one(cx);
45
46 llvm::add_module_flag_u32(cx.llmod(), llvm::ModuleFlagMergeBehavior::Max, "openmp", 51);
49
50 OffloadGlobals {
51 launcher_fn,
52 launcher_ty,
53 kernel_args_ty,
54 offload_entry_ty,
55 begin_mapper,
56 end_mapper,
57 mapper_fn_ty,
58 ident_t_global,
59 }
60 }
61}
62
63pub(crate) struct OffloadKernelDims<'ll> {
64 num_workgroups: Value<'ll>,
65 threads_per_block: Value<'ll>,
66 workgroup_dims: [Value<'ll>; 3],
67 thread_dims: [Value<'ll>; 3],
68}
69
70impl<'ll> OffloadKernelDims<'ll> {
71 pub(crate) fn from_operands<'tcx>(
72 builder: &mut Builder<'_, 'll, 'tcx>,
73 workgroup_op: &OperandRef<'tcx, llvm::Value<'ll>>,
74 thread_op: &OperandRef<'tcx, llvm::Value<'ll>>,
75 ) -> Self {
76 fn load_3_u32<'ll>(
77 builder: &mut Builder<'_, 'll, '_>,
78 place: PlaceValue<Value<'ll>>,
79 ) -> [Value<'ll>; 3] {
80 let ty = builder.type_i32();
81 let size = 4;
82 let v0 = builder.load(ty, place.llval, place.align);
83 let p1 = builder.inbounds_ptradd(place.llval, builder.const_usize(size));
84 let v1 = builder.load(ty, p1, place.align.restrict_for_offset(Size::from_bytes(size)));
85 let p2 = builder.inbounds_ptradd(place.llval, builder.const_usize(size * 2));
86 let v2 =
87 builder.load(ty, p2, place.align.restrict_for_offset(Size::from_bytes(size * 2)));
88 [v0, v1, v2]
89 }
90
91 let OperandValue::Ref(place) = workgroup_op.val else {
92 ::rustc_span::macros::bug_impl(None,
format_args!("expected array operand by reference"), Location::caller());bug!("expected array operand by reference");
93 };
94 let workgroup_val = load_3_u32(builder, place);
95
96 let OperandValue::Ref(place) = thread_op.val else {
97 ::rustc_span::macros::bug_impl(None,
format_args!("expected array operand by reference"), Location::caller());bug!("expected array operand by reference");
98 };
99 let thread_val = load_3_u32(builder, place);
100
101 fn mul_dim3<'ll, 'tcx>(
102 builder: &mut Builder<'_, 'll, 'tcx>,
103 [x, y, z]: [Value<'ll>; 3],
104 ) -> Value<'ll> {
105 let xy = builder.mul(x, y);
106 builder.mul(xy, z)
107 }
108
109 let num_workgroups = mul_dim3(builder, workgroup_val);
110 let threads_per_block = mul_dim3(builder, thread_val);
111
112 OffloadKernelDims {
113 workgroup_dims: workgroup_val,
114 thread_dims: thread_val,
115 num_workgroups,
116 threads_per_block,
117 }
118 }
119}
120
121fn generate_launcher<'ll>(cx: &CodegenCx<'ll, '_>) -> (llvm::Value<'ll>, llvm::Type<'ll>) {
124 let tptr = cx.type_ptr();
125 let ti64 = cx.type_i64();
126 let ti32 = cx.type_i32();
127 let args = ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[tptr, ti64, ti32, ti32, tptr, tptr]))vec![tptr, ti64, ti32, ti32, tptr, tptr];
128 let tgt_fn_ty = cx.type_func(&args, ti32);
129 let name = "__tgt_target_kernel";
130 let tgt_decl = declare_offload_fn(cx, name, tgt_fn_ty);
131 let nounwind = llvm::AttributeKind::NoUnwind.create_attr(cx.llcx);
132 attributes::apply_to_llfn(tgt_decl, Function, &[nounwind]);
133 (tgt_decl, tgt_fn_ty)
134}
135
136pub(crate) fn declare_omp_get_num_devices<'ll>(
139 cx: &CodegenCx<'ll, '_>,
140) -> (llvm::Value<'ll>, llvm::Type<'ll>) {
141 let ti32 = cx.type_i32();
142 let tgt_fn_ty = cx.type_func(&[], ti32);
143 let name = "omp_get_num_devices";
144 let tgt_decl = declare_offload_fn(cx, name, tgt_fn_ty);
145 let nounwind = llvm::AttributeKind::NoUnwind.create_attr(cx.llcx);
146 attributes::apply_to_llfn(tgt_decl, Function, &[nounwind]);
147 (tgt_decl, tgt_fn_ty)
148}
149
150pub(crate) fn generate_at_one<'ll>(cx: &CodegenCx<'ll, '_>) -> llvm::Value<'ll> {
156 let unknown_txt = ";unknown;unknown;0;0;;";
157 let c_entry_name = CString::new(unknown_txt).unwrap();
158 let c_val = c_entry_name.as_bytes_with_nul();
159 let initializer = crate::common::bytes_in_context(cx.llcx, c_val);
160 let at_zero = add_unnamed_global(cx, "", initializer, PrivateLinkage);
161 llvm::set_alignment(at_zero, Align::ONE);
162
163 let struct_ident_ty = cx.type_named_struct("struct.ident_t");
165 let struct_elems = ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[cx.get_const_i32(0), cx.get_const_i32(2), cx.get_const_i32(0),
cx.get_const_i32(22), at_zero]))vec![
166 cx.get_const_i32(0),
167 cx.get_const_i32(2),
168 cx.get_const_i32(0),
169 cx.get_const_i32(22),
170 at_zero,
171 ];
172 let struct_elems_ty: Vec<_> = struct_elems.iter().map(|&x| cx.val_ty(x)).collect();
173 let initializer = crate::common::named_struct(struct_ident_ty, &struct_elems);
174 cx.set_struct_body(struct_ident_ty, &struct_elems_ty);
175 let at_one = add_unnamed_global(cx, "", initializer, PrivateLinkage);
176 llvm::set_alignment(at_one, Align::EIGHT);
177 at_one
178}
179
180pub(crate) struct TgtOffloadEntry {
181 }
191
192impl TgtOffloadEntry {
193 pub(crate) fn new_decl<'ll>(cx: &CodegenCx<'ll, '_>) -> llvm::Type<'ll> {
194 let offload_entry_ty = cx.type_named_struct("struct.__tgt_offload_entry");
195 let tptr = cx.type_ptr();
196 let ti64 = cx.type_i64();
197 let ti32 = cx.type_i32();
198 let ti16 = cx.type_i16();
199 let entry_elements = ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[ti64, ti16, ti16, ti32, tptr, tptr, ti64, ti64, tptr]))vec![ti64, ti16, ti16, ti32, tptr, tptr, ti64, ti64, tptr];
202 cx.set_struct_body(offload_entry_ty, &entry_elements);
203 offload_entry_ty
204 }
205
206 fn new<'ll>(
207 cx: &CodegenCx<'ll, '_>,
208 region_id: Value<'ll>,
209 llglobal: Value<'ll>,
210 ) -> [Value<'ll>; 9] {
211 let reserved = cx.get_const_i64(0);
212 let version = cx.get_const_i16(1);
213 let kind = cx.get_const_i16(1);
214 let flags = cx.get_const_i32(0);
215 let size = cx.get_const_i64(0);
216 let data = cx.get_const_i64(0);
217 let aux_addr = cx.const_null(cx.type_ptr());
218 [reserved, version, kind, flags, region_id, llglobal, size, data, aux_addr]
219 }
220}
221
222pub(crate) struct KernelArgsTy {
224 pub field_offsets: [(&'static str, Size); Self::FIELD_COUNT],
225 pub size: Size,
226 pub align: Align,
227 }
249
250impl KernelArgsTy {
251 const FIELD_COUNT: usize = 17;
252 const OFFLOAD_VERSION: u64 = 3;
253 const FLAGS: u64 = 1 << 6; const TRIPCOUNT: u64 = 0;
255
256 fn new_decl<'ll>(cx: &CodegenCx<'ll, '_>) -> Self {
257 let uint32_t = Primitive::Int(Integer::I32, false);
258 let ptr = Primitive::Pointer(cx.data_layout().default_address_space);
259 let uint64_t = Primitive::Int(Integer::I64, false);
260 let mut current_offset = Size::ZERO;
261 let mut max_align = Align::ONE;
262 let field_offsets = [
263 ("Version", uint32_t),
264 ("NumArgs", uint32_t),
265 ("ArgBasePtrs", ptr),
266 ("ArgPtrs", ptr),
267 ("ArgSizes", ptr),
268 ("ArgTypes", ptr),
269 ("ArgNames", ptr),
270 ("ArgMappers", ptr),
271 ("Tripcount", uint64_t),
272 ("Flags", uint64_t),
273 ("NumTeamsX", uint32_t),
274 ("NumTeamsY", uint32_t),
275 ("NumTeamsZ", uint32_t),
276 ("ThreadLimitX", uint32_t),
277 ("ThreadLimitY", uint32_t),
278 ("ThreadLimitZ", uint32_t),
279 ("DynCGroupMem", uint32_t),
280 ]
281 .map(|(name, ty)| {
282 let align = ty.default_align(cx);
283 let this_offset = current_offset.align_to(align);
284 current_offset = this_offset + ty.size(cx);
285 max_align = max_align.max(align);
286 (name, this_offset)
287 });
288 Self { field_offsets, size: current_offset.align_to(max_align), align: max_align }
289 }
290
291 fn new<'ll, 'tcx>(
292 cx: &CodegenCx<'ll, 'tcx>,
293 num_args: u64,
294 memtransfer_types: Value<'ll>,
295 geps: [Value<'ll>; 3],
296 workgroup_dims: [Value<'ll>; 3],
297 thread_dims: [Value<'ll>; 3],
298 dyn_cache: Value<'ll>,
299 ) -> [Value<'ll>; Self::FIELD_COUNT] {
300 [
301 cx.get_const_i32(KernelArgsTy::OFFLOAD_VERSION),
303 cx.get_const_i32(num_args),
305 geps[0],
307 geps[1],
309 geps[2],
311 memtransfer_types,
313 cx.const_null(cx.type_ptr()), cx.const_null(cx.type_ptr()), cx.get_const_i64(KernelArgsTy::TRIPCOUNT),
320 cx.get_const_i64(KernelArgsTy::FLAGS),
322 workgroup_dims[0],
324 workgroup_dims[1],
325 workgroup_dims[2],
326 thread_dims[0],
328 thread_dims[1],
329 thread_dims[2],
330 dyn_cache,
332 ]
333 }
334}
335
336#[derive(#[automatically_derived]impl<'ll> ::core::marker::Copy for
OffloadKernelGlobals<'ll> {
}Copy, #[automatically_derived]#[doc(hidden)]unsafe impl<'ll>
::core::clone::TrivialClone for OffloadKernelGlobals<'ll> {
}
#[automatically_derived]impl<'ll> ::core::clone::Clone for
OffloadKernelGlobals<'ll> {
#[inline]
#[rustc_trivial_field_reads]
fn clone(&self) -> Self {
let _: ::core::clone::AssertParamIsClone<llvm::Value<'ll>>;
let _: ::core::clone::AssertParamIsClone<llvm::Value<'ll>>;
let _: ::core::clone::AssertParamIsClone<llvm::Value<'ll>>;
let _: ::core::clone::AssertParamIsClone<llvm::Value<'ll>>;
let _: ::core::clone::AssertParamIsClone<llvm::Value<'ll>>;
*self
}
}Clone)]
338pub(crate) struct OffloadKernelGlobals<'ll> {
339 pub offload_sizes: llvm::Value<'ll>,
340 pub memtransfer_begin: llvm::Value<'ll>,
341 pub memtransfer_kernel: llvm::Value<'ll>,
342 pub memtransfer_end: llvm::Value<'ll>,
343 pub region_id: llvm::Value<'ll>,
344}
345
346fn gen_tgt_data_mappers<'ll>(
347 cx: &CodegenCx<'ll, '_>,
348) -> (llvm::Value<'ll>, llvm::Value<'ll>, llvm::Value<'ll>, llvm::Type<'ll>) {
349 let tptr = cx.type_ptr();
350 let ti64 = cx.type_i64();
351 let ti32 = cx.type_i32();
352
353 let args = ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[tptr, ti64, ti32, tptr, tptr, tptr, tptr, tptr, tptr]))vec![tptr, ti64, ti32, tptr, tptr, tptr, tptr, tptr, tptr];
354 let mapper_fn_ty = cx.type_func(&args, cx.type_void());
355 let mapper_begin = "__tgt_target_data_begin_mapper";
356 let mapper_update = "__tgt_target_data_update_mapper";
357 let mapper_end = "__tgt_target_data_end_mapper";
358 let begin_mapper_decl = declare_offload_fn(cx, mapper_begin, mapper_fn_ty);
359 let update_mapper_decl = declare_offload_fn(cx, mapper_update, mapper_fn_ty);
360 let end_mapper_decl = declare_offload_fn(cx, mapper_end, mapper_fn_ty);
361
362 let nounwind = llvm::AttributeKind::NoUnwind.create_attr(cx.llcx);
363 attributes::apply_to_llfn(begin_mapper_decl, Function, &[nounwind]);
364 attributes::apply_to_llfn(update_mapper_decl, Function, &[nounwind]);
365 attributes::apply_to_llfn(end_mapper_decl, Function, &[nounwind]);
366
367 (begin_mapper_decl, update_mapper_decl, end_mapper_decl, mapper_fn_ty)
368}
369
370fn add_priv_unnamed_arr<'ll>(cx: &SimpleCx<'ll>, name: &str, vals: &[u64]) -> llvm::Value<'ll> {
371 let ti64 = cx.type_i64();
372 let mut size_val = Vec::with_capacity(vals.len());
373 for &val in vals {
374 size_val.push(cx.get_const_i64(val));
375 }
376 let initializer = cx.const_array(ti64, &size_val);
377 add_unnamed_global(cx, name, initializer, PrivateLinkage)
378}
379
380pub(crate) fn add_unnamed_global<'ll>(
381 cx: &SimpleCx<'ll>,
382 name: &str,
383 initializer: llvm::Value<'ll>,
384 l: Linkage,
385) -> llvm::Value<'ll> {
386 let llglobal = add_global(cx, name, initializer, l);
387 llvm::LLVMSetUnnamedAddress(llglobal, llvm::UnnamedAddr::Global);
388 llglobal
389}
390
391pub(crate) fn add_global<'ll>(
392 cx: &SimpleCx<'ll>,
393 name: &str,
394 initializer: llvm::Value<'ll>,
395 l: Linkage,
396) -> llvm::Value<'ll> {
397 let c_name = CString::new(name).unwrap();
398 let llglobal: llvm::Value<'ll> = llvm::add_global(cx.llmod, cx.val_ty(initializer), &c_name);
399 llvm::set_global_constant(llglobal, true);
400 llvm::set_linkage(llglobal, l);
401 llvm::set_initializer(llglobal, initializer);
402 llglobal
403}
404
405pub(crate) fn gen_define_handling<'ll>(
409 cx: &CodegenCx<'ll, '_>,
410 metadata: &[OffloadMetadata],
411 symbol: String,
412 offload_globals: &OffloadGlobals<'ll>,
413) -> OffloadKernelGlobals<'ll> {
414 if let Some(entry) = cx.offload_kernel_cache.borrow().get(&symbol) {
415 return *entry;
416 }
417
418 let offload_entry_ty = offload_globals.offload_entry_ty;
419
420 let (sizes, transfer): (Vec<_>, Vec<_>) =
421 metadata.iter().map(|m| (m.payload_size, m.mode)).unzip();
422 let handled_mappings = MappingFlags::TO
428 | MappingFlags::FROM
429 | MappingFlags::TARGET_PARAM
430 | MappingFlags::LITERAL
431 | MappingFlags::IMPLICIT;
432 for arg in &transfer {
433 if true {
if !!arg.contains_unknown_bits() {
::core::panicking::panic("assertion failed: !arg.contains_unknown_bits()")
};
};debug_assert!(!arg.contains_unknown_bits());
434 if true {
if !handled_mappings.contains(*arg) {
::core::panicking::panic("assertion failed: handled_mappings.contains(*arg)")
};
};debug_assert!(handled_mappings.contains(*arg));
435 }
436
437 let valid_begin_mappings = MappingFlags::TO | MappingFlags::LITERAL | MappingFlags::IMPLICIT;
438 let transfer_to: Vec<u64> =
439 transfer.iter().map(|m| m.intersection(valid_begin_mappings).bits()).collect();
440 let transfer_from: Vec<u64> =
441 transfer.iter().map(|m| m.intersection(MappingFlags::FROM).bits()).collect();
442 let valid_kernel_mappings = MappingFlags::LITERAL | MappingFlags::IMPLICIT;
443 let transfer_kernel: Vec<u64> = transfer
445 .iter()
446 .map(|m| (m.intersection(valid_kernel_mappings) | MappingFlags::TARGET_PARAM).bits())
447 .collect();
448
449 let actual_sizes = sizes
450 .iter()
451 .map(|s| match s {
452 OffloadSize::Static(sz) => *sz,
453 _ => 0,
455 })
456 .collect::<Vec<_>>();
457 let offload_sizes =
458 add_priv_unnamed_arr(cx, &::alloc::__export::must_use({
::alloc::fmt::format(format_args!(".offload_sizes.{0}", symbol))
})format!(".offload_sizes.{symbol}"), &actual_sizes);
459 let memtransfer_begin =
460 add_priv_unnamed_arr(cx, &::alloc::__export::must_use({
::alloc::fmt::format(format_args!(".offload_maptypes.{0}.begin",
symbol))
})format!(".offload_maptypes.{symbol}.begin"), &transfer_to);
461 let memtransfer_kernel =
462 add_priv_unnamed_arr(cx, &::alloc::__export::must_use({
::alloc::fmt::format(format_args!(".offload_maptypes.{0}.kernel",
symbol))
})format!(".offload_maptypes.{symbol}.kernel"), &transfer_kernel);
463 let memtransfer_end =
464 add_priv_unnamed_arr(cx, &::alloc::__export::must_use({
::alloc::fmt::format(format_args!(".offload_maptypes.{0}.end",
symbol))
})format!(".offload_maptypes.{symbol}.end"), &transfer_from);
465
466 let name = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(".{0}.region_id", symbol))
})format!(".{symbol}.region_id");
470 let initializer = cx.get_const_i8(0);
471 let region_id = add_global(cx, &name, initializer, WeakAnyLinkage);
472
473 let c_entry_name = CString::new(symbol.clone()).unwrap();
474 let c_val = c_entry_name.as_bytes_with_nul();
475 let offload_entry_name = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(".offloading.entry_name.{0}",
symbol))
})format!(".offloading.entry_name.{symbol}");
476
477 let initializer = crate::common::bytes_in_context(cx.llcx, c_val);
478 let llglobal = add_unnamed_global(cx, &offload_entry_name, initializer, InternalLinkage);
479 llvm::set_alignment(llglobal, Align::ONE);
480 llvm::set_section(llglobal, c".llvm.rodata.offloading");
481
482 let name = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(".offloading.entry.{0}", symbol))
})format!(".offloading.entry.{symbol}");
483
484 let elems = TgtOffloadEntry::new(cx, region_id, llglobal);
486
487 let initializer = crate::common::named_struct(offload_entry_ty, &elems);
488 let c_name = CString::new(name).unwrap();
489 let offload_entry = llvm::add_global(cx.llmod, offload_entry_ty, &c_name);
490 llvm::set_global_constant(offload_entry, true);
491 llvm::set_linkage(offload_entry, WeakAnyLinkage);
492 llvm::set_initializer(offload_entry, initializer);
493 llvm::set_alignment(offload_entry, Align::EIGHT);
494 let c_section_name = CString::new("llvm_offload_entries").unwrap();
495 llvm::set_section(offload_entry, &c_section_name);
496
497 cx.add_compiler_used_global(offload_entry);
498
499 let result = OffloadKernelGlobals {
500 offload_sizes,
501 memtransfer_begin,
502 memtransfer_kernel,
503 memtransfer_end,
504 region_id,
505 };
506
507 cx.offload_kernel_cache.borrow_mut().insert(symbol, result);
508
509 result
510}
511
512fn declare_offload_fn<'ll>(
513 cx: &CodegenCx<'ll, '_>,
514 name: &str,
515 ty: llvm::Type<'ll>,
516) -> llvm::Value<'ll> {
517 crate::declare::declare_simple_fn(
518 cx,
519 name,
520 llvm::CallConv::CCallConv,
521 llvm::UnnamedAddr::No,
522 llvm::Visibility::Default,
523 ty,
524 )
525}
526
527pub(crate) fn scalar_width<'ll>(cx: &SimpleCx<'ll>, ty: Type<'ll>) -> u64 {
528 if let Some(float) = cx.float_type(ty) {
529 float.size().bits()
530 } else if let Some(width) = cx.int_width(ty) {
531 width
532 } else if cx.type_kind(ty) == TypeKind::Pointer {
533 ::rustc_span::macros::bug_impl(None,
format_args!("scalar_width doesn\'t support pointer type {0:?}", ty),
Location::caller())bug!("scalar_width doesn't support pointer type {ty:?}")
534 } else {
535 ::rustc_span::macros::bug_impl(None,
format_args!("scalar_width was called on a non scalar type {0:?}", ty),
Location::caller())bug!("scalar_width was called on a non scalar type {ty:?}")
536 }
537}
538
539fn get_runtime_size<'ll, 'tcx>(
540 builder: &mut Builder<'_, 'll, 'tcx>,
541 args: &[Value<'ll>],
542 index: usize,
543 meta: &OffloadMetadata,
544) -> Value<'ll> {
545 match meta.payload_size {
546 OffloadSize::Slice { element_size } => {
547 let length_idx = index + 1;
548 let length = args[length_idx];
549 let length_i64 = builder.intcast(length, builder.cx.type_i64(), false);
550 builder.mul(length_i64, builder.cx.get_const_i64(element_size))
551 }
552 _ => ::rustc_span::macros::bug_impl(None,
format_args!("unexpected offload size {0:?}", meta.payload_size),
Location::caller())bug!("unexpected offload size {:?}", meta.payload_size),
553 }
554}
555
556pub(crate) fn gen_call_handling<'ll, 'tcx>(
575 builder: &mut Builder<'_, 'll, 'tcx>,
576 offload_data: &OffloadKernelGlobals<'ll>,
577 args: &[Value<'ll>],
578 types: &[Type<'ll>],
579 metadata: &[OffloadMetadata],
580 offload_globals: &OffloadGlobals<'ll>,
581 offload_dims: &OffloadKernelDims<'ll>,
582 dyn_cache: Value<'ll>,
583 device_id: Value<'ll>,
584) {
585 let cx = builder.cx;
586 let &OffloadKernelGlobals {
587 offload_sizes,
588 memtransfer_begin,
589 memtransfer_kernel,
590 memtransfer_end,
591 region_id,
592 } = offload_data;
593 let &OffloadKernelDims { num_workgroups, threads_per_block, workgroup_dims, thread_dims } =
594 offload_dims;
595
596 let has_dynamic = metadata.iter().any(|m| !#[allow(non_exhaustive_omitted_patterns)] match m.payload_size {
OffloadSize::Static(_) => true,
_ => false,
}matches!(m.payload_size, OffloadSize::Static(_)));
597
598 let tgt_decl = offload_globals.launcher_fn;
599 let tgt_target_kernel_ty = offload_globals.launcher_ty;
600
601 let tgt_kernel_decl = &offload_globals.kernel_args_ty;
602 let begin_mapper_decl = offload_globals.begin_mapper;
603 let end_mapper_decl = offload_globals.end_mapper;
604 let fn_ty = offload_globals.mapper_fn_ty;
605
606 let num_args = types.len() as u64;
607 let bb = builder.llbb();
608
609 unsafe {
611 llvm::LLVMRustPositionBuilderPastAllocas(builder.llbuilder(), builder.llfn());
612 }
613
614 let pointer_array_size = cx.data_layout().pointer_size() * num_args;
615 let a1 = builder.direct_alloca(pointer_array_size, Align::EIGHT, ".offload_baseptrs");
617 let a2 = builder.direct_alloca(pointer_array_size, Align::EIGHT, ".offload_ptrs");
619 let size_array_size = Size::from_bytes(8) * num_args;
621
622 let a4 = if has_dynamic {
623 let alloc = builder.direct_alloca(size_array_size, Align::EIGHT, ".offload_sizes");
624
625 builder.memcpy(
626 alloc,
627 Align::EIGHT,
628 offload_sizes,
629 Align::EIGHT,
630 cx.get_const_i64(8 * args.len() as u64),
631 MemFlags::empty(),
632 None,
633 );
634
635 alloc
636 } else {
637 offload_sizes
638 };
639
640 let a5 = builder.direct_alloca(tgt_kernel_decl.size, tgt_kernel_decl.align, "kernel_args");
642
643 unsafe {
645 llvm::LLVMPositionBuilderAtEnd(builder.llbuilder(), bb);
646 }
647
648 let mut vals = ::alloc::vec::Vec::new()vec![];
650 let mut geps = ::alloc::vec::Vec::new()vec![];
651 for &v in args {
652 let ty = cx.val_ty(v);
653 let ty_kind = cx.type_kind(ty);
654 let (base_val, gep_base) = match ty_kind {
655 TypeKind::Pointer => (v, v),
656 TypeKind::Half | TypeKind::Float | TypeKind::Double | TypeKind::Integer => {
657 let num_bits = scalar_width(cx, ty);
659
660 let bb = builder.llbb();
661 unsafe {
662 llvm::LLVMRustPositionBuilderPastAllocas(builder.llbuilder(), builder.llfn());
663 }
664 let addr = builder.direct_alloca(Size::from_bytes(8), Align::EIGHT, "addr");
665 unsafe {
666 llvm::LLVMPositionBuilderAtEnd(builder.llbuilder(), bb);
667 }
668
669 let cast = builder.bitcast(v, cx.type_ix(num_bits));
670 let value = builder.zext(cast, cx.type_i64());
671 builder.store(value, addr, Align::EIGHT);
672 (value, addr)
673 }
674 other => ::rustc_span::macros::bug_impl(None,
format_args!("offload does not support {0:?}", other), Location::caller())bug!("offload does not support {other:?}"),
675 };
676
677 vals.push(base_val);
678 geps.push(gep_base);
679 }
680
681 for i in 0..num_args {
682 let pointer_offset = cx.const_usize((cx.data_layout().pointer_size() * i).bytes());
683 let gep1 = builder.inbounds_ptradd(a1, pointer_offset);
684 builder.store(vals[i as usize], gep1, Align::EIGHT);
685 let gep2 = builder.inbounds_ptradd(a2, pointer_offset);
686 builder.store(geps[i as usize], gep2, Align::EIGHT);
687
688 if !#[allow(non_exhaustive_omitted_patterns)] match metadata[i as
usize].payload_size {
OffloadSize::Static(_) => true,
_ => false,
}matches!(metadata[i as usize].payload_size, OffloadSize::Static(_)) {
689 let gep3 = builder.inbounds_ptradd(a4, cx.const_usize(8 * i));
690 let size_val = get_runtime_size(builder, args, i as usize, &metadata[i as usize]);
691 builder.store(size_val, gep3, Align::EIGHT);
692 }
693 }
694
695 fn get_geps<'ll, 'tcx>(
698 _builder: &mut Builder<'_, 'll, 'tcx>,
699 a1: Value<'ll>,
700 a2: Value<'ll>,
701 a4: Value<'ll>,
702 _is_dynamic: bool,
703 ) -> [Value<'ll>; 3] {
704 [a1, a2, a4]
705 }
706
707 fn generate_mapper_call<'ll, 'tcx>(
708 builder: &mut Builder<'_, 'll, 'tcx>,
709 geps: [Value<'ll>; 3],
710 o_type: Value<'ll>,
711 fn_to_call: Value<'ll>,
712 fn_ty: Type<'ll>,
713 num_args: u64,
714 s_ident_t: Value<'ll>,
715 ) {
716 let cx = builder.cx;
717 let nullptr = cx.const_null(cx.type_ptr());
718 let i64_max = cx.get_const_i64(u64::MAX);
719 let num_args = cx.get_const_i32(num_args);
720 let args =
721 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[s_ident_t, i64_max, num_args, geps[0], geps[1], geps[2], o_type,
nullptr, nullptr]))vec![s_ident_t, i64_max, num_args, geps[0], geps[1], geps[2], o_type, nullptr, nullptr];
722 builder.call(fn_ty, None, None, fn_to_call, ReturnSlot::Direct, &args, None, None);
723 }
724
725 let s_ident_t = offload_globals.ident_t_global;
727 let geps = get_geps(builder, a1, a2, a4, has_dynamic);
728 generate_mapper_call(
729 builder,
730 geps,
731 memtransfer_begin,
732 begin_mapper_decl,
733 fn_ty,
734 num_args,
735 s_ident_t,
736 );
737 let values = KernelArgsTy::new(
738 cx,
739 num_args,
740 memtransfer_kernel,
741 geps,
742 workgroup_dims,
743 thread_dims,
744 dyn_cache,
745 );
746
747 for ((name, offset), value) in tgt_kernel_decl.field_offsets.into_iter().zip(values) {
750 let ptr = if offset.bytes() == 0 {
751 a5
752 } else {
753 let ptr = builder.inbounds_ptradd(a5, cx.const_usize(offset.bytes()));
754 llvm::set_value_name(ptr, name.as_bytes());
755 ptr
756 };
757
758 builder.store(value, ptr, tgt_kernel_decl.align.restrict_for_offset(offset));
759 }
760
761 let device_id = builder.sext(device_id, cx.type_i64());
762 let args = ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[s_ident_t, device_id, num_workgroups, threads_per_block, region_id,
a5]))vec![s_ident_t, device_id, num_workgroups, threads_per_block, region_id, a5];
763 builder.call(tgt_target_kernel_ty, None, None, tgt_decl, ReturnSlot::Direct, &args, None, None);
764 let geps = get_geps(builder, a1, a2, a4, has_dynamic);
768 generate_mapper_call(
769 builder,
770 geps,
771 memtransfer_end,
772 end_mapper_decl,
773 fn_ty,
774 num_args,
775 s_ident_t,
776 );
777}