1use std::ffi::CString;
2
3use bitflags::Flags;
4use llvm::Linkage::*;
5use rustc_abi::Align;
6use rustc_codegen_ssa::MemFlags;
7use rustc_codegen_ssa::common::TypeKind;
8use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue};
9use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, BuilderMethods};
10use rustc_middle::bug;
11use rustc_middle::ty::offload_meta::{MappingFlags, OffloadMetadata, OffloadSize};
12
13use crate::builder::Builder;
14use crate::common::CodegenCx;
15use crate::llvm::AttributePlace::Function;
16use crate::llvm::{self, Linkage, Type, Value};
17use crate::{SimpleCx, attributes};
18
19pub(crate) struct OffloadGlobals<'ll> {
21 pub launcher_fn: &'ll llvm::Value,
22 pub launcher_ty: &'ll llvm::Type,
23
24 pub kernel_args_ty: &'ll llvm::Type,
25
26 pub offload_entry_ty: &'ll llvm::Type,
27
28 pub begin_mapper: &'ll llvm::Value,
29 pub end_mapper: &'ll llvm::Value,
30 pub mapper_fn_ty: &'ll llvm::Type,
31
32 pub ident_t_global: &'ll llvm::Value,
33}
34
35impl<'ll> OffloadGlobals<'ll> {
36 pub(crate) fn declare(cx: &CodegenCx<'ll, '_>) -> Self {
37 let (launcher_fn, launcher_ty) = generate_launcher(cx);
38 let kernel_args_ty = KernelArgsTy::new_decl(cx);
39 let offload_entry_ty = TgtOffloadEntry::new_decl(cx);
40 let (begin_mapper, _, end_mapper, mapper_fn_ty) = gen_tgt_data_mappers(cx);
41 let ident_t_global = generate_at_one(cx);
42
43 llvm::add_module_flag_u32(cx.llmod(), llvm::ModuleFlagMergeBehavior::Max, "openmp", 51);
46
47 OffloadGlobals {
48 launcher_fn,
49 launcher_ty,
50 kernel_args_ty,
51 offload_entry_ty,
52 begin_mapper,
53 end_mapper,
54 mapper_fn_ty,
55 ident_t_global,
56 }
57 }
58}
59
60pub(crate) fn register_offload<'ll>(cx: &CodegenCx<'ll, '_>) {
67 let register_lib_name = "__tgt_register_lib";
70 if cx.get_function(register_lib_name).is_some() {
71 return;
72 }
73
74 let reg_lib_decl = cx.type_func(&[cx.type_ptr()], cx.type_void());
75 let register_lib = declare_offload_fn(&cx, register_lib_name, reg_lib_decl);
76 let unregister_lib = declare_offload_fn(&cx, "__tgt_unregister_lib", reg_lib_decl);
77
78 let ptr_null = cx.const_null(cx.type_ptr());
79 let const_struct = cx.const_struct(&[cx.get_const_i32(0), ptr_null, ptr_null, ptr_null], false);
80 let omp_descriptor =
81 add_global(cx, ".omp_offloading.descriptor", const_struct, InternalLinkage);
82 let atexit = cx.type_func(&[cx.type_ptr()], cx.type_i32());
86 let atexit_fn = declare_offload_fn(cx, "atexit", atexit);
87
88 let init_ty = cx.type_func(&[], cx.type_void());
91 let init_rtls = declare_offload_fn(cx, "__tgt_init_all_rtls", init_ty);
92
93 let desc_ty = cx.type_func(&[], cx.type_void());
94 let reg_name = ".omp_offloading.descriptor_reg";
95 let unreg_name = ".omp_offloading.descriptor_unreg";
96 let desc_reg_fn = declare_offload_fn(cx, reg_name, desc_ty);
97 let desc_unreg_fn = declare_offload_fn(cx, unreg_name, desc_ty);
98 llvm::set_linkage(desc_reg_fn, InternalLinkage);
99 llvm::set_linkage(desc_unreg_fn, InternalLinkage);
100 llvm::set_section(desc_reg_fn, c".text.startup");
101 llvm::set_section(desc_unreg_fn, c".text.startup");
102
103 let bb = Builder::append_block(cx, desc_reg_fn, "entry");
111 let mut a = Builder::build(cx, bb);
112 a.call(reg_lib_decl, None, None, register_lib, &[omp_descriptor], None, None);
113 a.call(init_ty, None, None, init_rtls, &[], None, None);
114 a.call(atexit, None, None, atexit_fn, &[desc_unreg_fn], None, None);
115 a.ret_void();
116
117 let bb = Builder::append_block(cx, desc_unreg_fn, "entry");
123 let mut a = Builder::build(cx, bb);
124 a.call(reg_lib_decl, None, None, unregister_lib, &[omp_descriptor], None, None);
125 a.ret_void();
126
127 let args = ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[cx.get_const_i32(101), desc_reg_fn, ptr_null]))vec![cx.get_const_i32(101), desc_reg_fn, ptr_null];
129 let const_struct = cx.const_struct(&args, false);
130 let arr = cx.const_array(cx.val_ty(const_struct), &[const_struct]);
131 add_global(cx, "llvm.global_ctors", arr, AppendingLinkage);
132}
133
134pub(crate) struct OffloadKernelDims<'ll> {
135 num_workgroups: &'ll Value,
136 threads_per_block: &'ll Value,
137 workgroup_dims: &'ll Value,
138 thread_dims: &'ll Value,
139}
140
141impl<'ll> OffloadKernelDims<'ll> {
142 pub(crate) fn from_operands<'tcx>(
143 builder: &mut Builder<'_, 'll, 'tcx>,
144 workgroup_op: &OperandRef<'tcx, &'ll llvm::Value>,
145 thread_op: &OperandRef<'tcx, &'ll llvm::Value>,
146 ) -> Self {
147 let cx = builder.cx;
148 let arr_ty = cx.type_array(cx.type_i32(), 3);
149 let four = Align::from_bytes(4).unwrap();
150
151 let OperandValue::Ref(place) = workgroup_op.val else {
152 ::rustc_middle::util::bug::bug_fmt(format_args!("expected array operand by reference"));bug!("expected array operand by reference");
153 };
154 let workgroup_val = builder.load(arr_ty, place.llval, four);
155
156 let OperandValue::Ref(place) = thread_op.val else {
157 ::rustc_middle::util::bug::bug_fmt(format_args!("expected array operand by reference"));bug!("expected array operand by reference");
158 };
159 let thread_val = builder.load(arr_ty, place.llval, four);
160
161 fn mul_dim3<'ll, 'tcx>(
162 builder: &mut Builder<'_, 'll, 'tcx>,
163 arr: &'ll Value,
164 ) -> &'ll Value {
165 let x = builder.extract_value(arr, 0);
166 let y = builder.extract_value(arr, 1);
167 let z = builder.extract_value(arr, 2);
168
169 let xy = builder.mul(x, y);
170 builder.mul(xy, z)
171 }
172
173 let num_workgroups = mul_dim3(builder, workgroup_val);
174 let threads_per_block = mul_dim3(builder, thread_val);
175
176 OffloadKernelDims {
177 workgroup_dims: workgroup_val,
178 thread_dims: thread_val,
179 num_workgroups,
180 threads_per_block,
181 }
182 }
183}
184
185fn generate_launcher<'ll>(cx: &CodegenCx<'ll, '_>) -> (&'ll llvm::Value, &'ll llvm::Type) {
188 let tptr = cx.type_ptr();
189 let ti64 = cx.type_i64();
190 let ti32 = cx.type_i32();
191 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];
192 let tgt_fn_ty = cx.type_func(&args, ti32);
193 let name = "__tgt_target_kernel";
194 let tgt_decl = declare_offload_fn(&cx, name, tgt_fn_ty);
195 let nounwind = llvm::AttributeKind::NoUnwind.create_attr(cx.llcx);
196 attributes::apply_to_llfn(tgt_decl, Function, &[nounwind]);
197 (tgt_decl, tgt_fn_ty)
198}
199
200pub(crate) fn declare_omp_get_num_devices<'ll>(
203 cx: &CodegenCx<'ll, '_>,
204) -> (&'ll llvm::Value, &'ll llvm::Type) {
205 let ti32 = cx.type_i32();
206 let tgt_fn_ty = cx.type_func(&[], ti32);
207 let name = "omp_get_num_devices";
208 let tgt_decl = declare_offload_fn(&cx, name, tgt_fn_ty);
209 let nounwind = llvm::AttributeKind::NoUnwind.create_attr(cx.llcx);
210 attributes::apply_to_llfn(tgt_decl, Function, &[nounwind]);
211 (tgt_decl, tgt_fn_ty)
212}
213
214pub(crate) fn generate_at_one<'ll>(cx: &CodegenCx<'ll, '_>) -> &'ll llvm::Value {
220 let unknown_txt = ";unknown;unknown;0;0;;";
221 let c_entry_name = CString::new(unknown_txt).unwrap();
222 let c_val = c_entry_name.as_bytes_with_nul();
223 let initializer = crate::common::bytes_in_context(cx.llcx, c_val);
224 let at_zero = add_unnamed_global(&cx, &"", initializer, PrivateLinkage);
225 llvm::set_alignment(at_zero, Align::ONE);
226
227 let struct_ident_ty = cx.type_named_struct("struct.ident_t");
229 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![
230 cx.get_const_i32(0),
231 cx.get_const_i32(2),
232 cx.get_const_i32(0),
233 cx.get_const_i32(22),
234 at_zero,
235 ];
236 let struct_elems_ty: Vec<_> = struct_elems.iter().map(|&x| cx.val_ty(x)).collect();
237 let initializer = crate::common::named_struct(struct_ident_ty, &struct_elems);
238 cx.set_struct_body(struct_ident_ty, &struct_elems_ty, false);
239 let at_one = add_unnamed_global(&cx, &"", initializer, PrivateLinkage);
240 llvm::set_alignment(at_one, Align::EIGHT);
241 at_one
242}
243
244pub(crate) struct TgtOffloadEntry {
245 }
255
256impl TgtOffloadEntry {
257 pub(crate) fn new_decl<'ll>(cx: &CodegenCx<'ll, '_>) -> &'ll llvm::Type {
258 let offload_entry_ty = cx.type_named_struct("struct.__tgt_offload_entry");
259 let tptr = cx.type_ptr();
260 let ti64 = cx.type_i64();
261 let ti32 = cx.type_i32();
262 let ti16 = cx.type_i16();
263 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];
266 cx.set_struct_body(offload_entry_ty, &entry_elements, false);
267 offload_entry_ty
268 }
269
270 fn new<'ll>(
271 cx: &CodegenCx<'ll, '_>,
272 region_id: &'ll Value,
273 llglobal: &'ll Value,
274 ) -> [&'ll Value; 9] {
275 let reserved = cx.get_const_i64(0);
276 let version = cx.get_const_i16(1);
277 let kind = cx.get_const_i16(1);
278 let flags = cx.get_const_i32(0);
279 let size = cx.get_const_i64(0);
280 let data = cx.get_const_i64(0);
281 let aux_addr = cx.const_null(cx.type_ptr());
282 [reserved, version, kind, flags, region_id, llglobal, size, data, aux_addr]
283 }
284}
285
286struct KernelArgsTy {
288 }
310
311impl KernelArgsTy {
312 const OFFLOAD_VERSION: u64 = 3;
313 const FLAGS: u64 = 1 << 6; const TRIPCOUNT: u64 = 0;
315 fn new_decl<'ll>(cx: &CodegenCx<'ll, '_>) -> &'ll Type {
316 let kernel_arguments_ty = cx.type_named_struct("struct.__tgt_kernel_arguments");
317 let tptr = cx.type_ptr();
318 let ti64 = cx.type_i64();
319 let ti32 = cx.type_i32();
320 let tarr = cx.type_array(ti32, 3);
321
322 let kernel_elements =
323 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[ti32, ti32, tptr, tptr, tptr, tptr, tptr, tptr, ti64, ti64, tarr,
tarr, ti32]))vec![ti32, ti32, tptr, tptr, tptr, tptr, tptr, tptr, ti64, ti64, tarr, tarr, ti32];
324
325 cx.set_struct_body(kernel_arguments_ty, &kernel_elements, false);
326 kernel_arguments_ty
327 }
328
329 fn new<'ll, 'tcx>(
330 cx: &CodegenCx<'ll, 'tcx>,
331 num_args: u64,
332 memtransfer_types: &'ll Value,
333 geps: [&'ll Value; 3],
334 workgroup_dims: &'ll Value,
335 thread_dims: &'ll Value,
336 dyn_cache: &'ll Value,
337 ) -> [(Align, &'ll str, &'ll Value); 13] {
338 let four = Align::from_bytes(4).expect("4 Byte alignment should work");
339 let eight = Align::EIGHT;
340
341 [
342 (four, "Version", cx.get_const_i32(KernelArgsTy::OFFLOAD_VERSION)),
343 (four, "NumArgs", cx.get_const_i32(num_args)),
344 (eight, "ArgBasePtrs", geps[0]),
345 (eight, "ArgPtrs", geps[1]),
346 (eight, "ArgSizes", geps[2]),
347 (eight, "ArgTypes", memtransfer_types),
348 (eight, "ArgNames", cx.const_null(cx.type_ptr())), (eight, "ArgMappers", cx.const_null(cx.type_ptr())), (eight, "Tripcount", cx.get_const_i64(KernelArgsTy::TRIPCOUNT)),
352 (eight, "Flags", cx.get_const_i64(KernelArgsTy::FLAGS)),
353 (four, "NumTeams", workgroup_dims),
354 (four, "ThreadLimit", thread_dims),
355 (four, "DynCGroupMem", dyn_cache),
356 ]
357 }
358}
359
360#[derive(#[automatically_derived]
impl<'ll> ::core::marker::Copy for OffloadKernelGlobals<'ll> { }Copy, #[automatically_derived]
impl<'ll> ::core::clone::Clone for OffloadKernelGlobals<'ll> {
#[inline]
fn clone(&self) -> OffloadKernelGlobals<'ll> {
let _: ::core::clone::AssertParamIsClone<&'ll llvm::Value>;
let _: ::core::clone::AssertParamIsClone<&'ll llvm::Value>;
let _: ::core::clone::AssertParamIsClone<&'ll llvm::Value>;
let _: ::core::clone::AssertParamIsClone<&'ll llvm::Value>;
let _: ::core::clone::AssertParamIsClone<&'ll llvm::Value>;
*self
}
}Clone)]
362pub(crate) struct OffloadKernelGlobals<'ll> {
363 pub offload_sizes: &'ll llvm::Value,
364 pub memtransfer_begin: &'ll llvm::Value,
365 pub memtransfer_kernel: &'ll llvm::Value,
366 pub memtransfer_end: &'ll llvm::Value,
367 pub region_id: &'ll llvm::Value,
368}
369
370fn gen_tgt_data_mappers<'ll>(
371 cx: &CodegenCx<'ll, '_>,
372) -> (&'ll llvm::Value, &'ll llvm::Value, &'ll llvm::Value, &'ll llvm::Type) {
373 let tptr = cx.type_ptr();
374 let ti64 = cx.type_i64();
375 let ti32 = cx.type_i32();
376
377 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];
378 let mapper_fn_ty = cx.type_func(&args, cx.type_void());
379 let mapper_begin = "__tgt_target_data_begin_mapper";
380 let mapper_update = "__tgt_target_data_update_mapper";
381 let mapper_end = "__tgt_target_data_end_mapper";
382 let begin_mapper_decl = declare_offload_fn(&cx, mapper_begin, mapper_fn_ty);
383 let update_mapper_decl = declare_offload_fn(&cx, mapper_update, mapper_fn_ty);
384 let end_mapper_decl = declare_offload_fn(&cx, mapper_end, mapper_fn_ty);
385
386 let nounwind = llvm::AttributeKind::NoUnwind.create_attr(cx.llcx);
387 attributes::apply_to_llfn(begin_mapper_decl, Function, &[nounwind]);
388 attributes::apply_to_llfn(update_mapper_decl, Function, &[nounwind]);
389 attributes::apply_to_llfn(end_mapper_decl, Function, &[nounwind]);
390
391 (begin_mapper_decl, update_mapper_decl, end_mapper_decl, mapper_fn_ty)
392}
393
394fn add_priv_unnamed_arr<'ll>(cx: &SimpleCx<'ll>, name: &str, vals: &[u64]) -> &'ll llvm::Value {
395 let ti64 = cx.type_i64();
396 let mut size_val = Vec::with_capacity(vals.len());
397 for &val in vals {
398 size_val.push(cx.get_const_i64(val));
399 }
400 let initializer = cx.const_array(ti64, &size_val);
401 add_unnamed_global(cx, name, initializer, PrivateLinkage)
402}
403
404pub(crate) fn add_unnamed_global<'ll>(
405 cx: &SimpleCx<'ll>,
406 name: &str,
407 initializer: &'ll llvm::Value,
408 l: Linkage,
409) -> &'ll llvm::Value {
410 let llglobal = add_global(cx, name, initializer, l);
411 llvm::LLVMSetUnnamedAddress(llglobal, llvm::UnnamedAddr::Global);
412 llglobal
413}
414
415pub(crate) fn add_global<'ll>(
416 cx: &SimpleCx<'ll>,
417 name: &str,
418 initializer: &'ll llvm::Value,
419 l: Linkage,
420) -> &'ll llvm::Value {
421 let c_name = CString::new(name).unwrap();
422 let llglobal: &'ll llvm::Value = llvm::add_global(cx.llmod, cx.val_ty(initializer), &c_name);
423 llvm::set_global_constant(llglobal, true);
424 llvm::set_linkage(llglobal, l);
425 llvm::set_initializer(llglobal, initializer);
426 llglobal
427}
428
429pub(crate) fn gen_define_handling<'ll>(
433 cx: &CodegenCx<'ll, '_>,
434 metadata: &[OffloadMetadata],
435 symbol: String,
436 offload_globals: &OffloadGlobals<'ll>,
437) -> OffloadKernelGlobals<'ll> {
438 if let Some(entry) = cx.offload_kernel_cache.borrow().get(&symbol) {
439 return *entry;
440 }
441
442 let offload_entry_ty = offload_globals.offload_entry_ty;
443
444 let (sizes, transfer): (Vec<_>, Vec<_>) =
445 metadata.iter().map(|m| (m.payload_size, m.mode)).unzip();
446 let handled_mappings = MappingFlags::TO
452 | MappingFlags::FROM
453 | MappingFlags::TARGET_PARAM
454 | MappingFlags::LITERAL
455 | MappingFlags::IMPLICIT;
456 for arg in &transfer {
457 if true {
if !!arg.contains_unknown_bits() {
::core::panicking::panic("assertion failed: !arg.contains_unknown_bits()")
};
};debug_assert!(!arg.contains_unknown_bits());
458 if true {
if !handled_mappings.contains(*arg) {
::core::panicking::panic("assertion failed: handled_mappings.contains(*arg)")
};
};debug_assert!(handled_mappings.contains(*arg));
459 }
460
461 let valid_begin_mappings = MappingFlags::TO | MappingFlags::LITERAL | MappingFlags::IMPLICIT;
462 let transfer_to: Vec<u64> =
463 transfer.iter().map(|m| m.intersection(valid_begin_mappings).bits()).collect();
464 let transfer_from: Vec<u64> =
465 transfer.iter().map(|m| m.intersection(MappingFlags::FROM).bits()).collect();
466 let valid_kernel_mappings = MappingFlags::LITERAL | MappingFlags::IMPLICIT;
467 let transfer_kernel: Vec<u64> = transfer
469 .iter()
470 .map(|m| (m.intersection(valid_kernel_mappings) | MappingFlags::TARGET_PARAM).bits())
471 .collect();
472
473 let actual_sizes = sizes
474 .iter()
475 .map(|s| match s {
476 OffloadSize::Static(sz) => *sz,
477 _ => 0,
479 })
480 .collect::<Vec<_>>();
481 let offload_sizes =
482 add_priv_unnamed_arr(&cx, &::alloc::__export::must_use({
::alloc::fmt::format(format_args!(".offload_sizes.{0}", symbol))
})format!(".offload_sizes.{symbol}"), &actual_sizes);
483 let memtransfer_begin =
484 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);
485 let memtransfer_kernel =
486 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);
487 let memtransfer_end =
488 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);
489
490 let name = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(".{0}.region_id", symbol))
})format!(".{symbol}.region_id");
494 let initializer = cx.get_const_i8(0);
495 let region_id = add_global(&cx, &name, initializer, WeakAnyLinkage);
496
497 let c_entry_name = CString::new(symbol.clone()).unwrap();
498 let c_val = c_entry_name.as_bytes_with_nul();
499 let offload_entry_name = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(".offloading.entry_name.{0}",
symbol))
})format!(".offloading.entry_name.{symbol}");
500
501 let initializer = crate::common::bytes_in_context(cx.llcx, c_val);
502 let llglobal = add_unnamed_global(&cx, &offload_entry_name, initializer, InternalLinkage);
503 llvm::set_alignment(llglobal, Align::ONE);
504 llvm::set_section(llglobal, c".llvm.rodata.offloading");
505
506 let name = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(".offloading.entry.{0}", symbol))
})format!(".offloading.entry.{symbol}");
507
508 let elems = TgtOffloadEntry::new(&cx, region_id, llglobal);
510
511 let initializer = crate::common::named_struct(offload_entry_ty, &elems);
512 let c_name = CString::new(name).unwrap();
513 let offload_entry = llvm::add_global(cx.llmod, offload_entry_ty, &c_name);
514 llvm::set_global_constant(offload_entry, true);
515 llvm::set_linkage(offload_entry, WeakAnyLinkage);
516 llvm::set_initializer(offload_entry, initializer);
517 llvm::set_alignment(offload_entry, Align::EIGHT);
518 let c_section_name = CString::new("llvm_offload_entries").unwrap();
519 llvm::set_section(offload_entry, &c_section_name);
520
521 cx.add_compiler_used_global(offload_entry);
522
523 let result = OffloadKernelGlobals {
524 offload_sizes,
525 memtransfer_begin,
526 memtransfer_kernel,
527 memtransfer_end,
528 region_id,
529 };
530
531 cx.offload_kernel_cache.borrow_mut().insert(symbol, result);
532
533 result
534}
535
536fn declare_offload_fn<'ll>(
537 cx: &CodegenCx<'ll, '_>,
538 name: &str,
539 ty: &'ll llvm::Type,
540) -> &'ll llvm::Value {
541 crate::declare::declare_simple_fn(
542 cx,
543 name,
544 llvm::CallConv::CCallConv,
545 llvm::UnnamedAddr::No,
546 llvm::Visibility::Default,
547 ty,
548 )
549}
550
551pub(crate) fn scalar_width<'ll>(cx: &'ll SimpleCx<'_>, ty: &'ll Type) -> u64 {
552 match cx.type_kind(ty) {
553 TypeKind::Half
554 | TypeKind::Float
555 | TypeKind::Double
556 | TypeKind::X86_FP80
557 | TypeKind::FP128
558 | TypeKind::PPC_FP128 => cx.float_width(ty) as u64,
559 TypeKind::Integer => cx.int_width(ty),
560 other => ::rustc_middle::util::bug::bug_fmt(format_args!("scalar_width was called on a non scalar type {0:?}",
other))bug!("scalar_width was called on a non scalar type {other:?}"),
561 }
562}
563
564fn get_runtime_size<'ll, 'tcx>(
565 builder: &mut Builder<'_, 'll, 'tcx>,
566 args: &[&'ll Value],
567 index: usize,
568 meta: &OffloadMetadata,
569) -> &'ll Value {
570 match meta.payload_size {
571 OffloadSize::Slice { element_size } => {
572 let length_idx = index + 1;
573 let length = args[length_idx];
574 let length_i64 = builder.intcast(length, builder.cx.type_i64(), false);
575 builder.mul(length_i64, builder.cx.get_const_i64(element_size))
576 }
577 _ => ::rustc_middle::util::bug::bug_fmt(format_args!("unexpected offload size {0:?}",
meta.payload_size))bug!("unexpected offload size {:?}", meta.payload_size),
578 }
579}
580
581pub(crate) fn gen_call_handling<'ll, 'tcx>(
600 builder: &mut Builder<'_, 'll, 'tcx>,
601 offload_data: &OffloadKernelGlobals<'ll>,
602 args: &[&'ll Value],
603 types: &[&Type],
604 metadata: &[OffloadMetadata],
605 offload_globals: &OffloadGlobals<'ll>,
606 offload_dims: &OffloadKernelDims<'ll>,
607 dyn_cache: &'ll Value,
608 device_id: &'ll Value,
609) {
610 let cx = builder.cx;
611 let OffloadKernelGlobals {
612 offload_sizes,
613 memtransfer_begin,
614 memtransfer_kernel,
615 memtransfer_end,
616 region_id,
617 } = offload_data;
618 let OffloadKernelDims { num_workgroups, threads_per_block, workgroup_dims, thread_dims } =
619 offload_dims;
620
621 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(_)));
622
623 let tgt_decl = offload_globals.launcher_fn;
624 let tgt_target_kernel_ty = offload_globals.launcher_ty;
625
626 let tgt_kernel_decl = offload_globals.kernel_args_ty;
627 let begin_mapper_decl = offload_globals.begin_mapper;
628 let end_mapper_decl = offload_globals.end_mapper;
629 let fn_ty = offload_globals.mapper_fn_ty;
630
631 let num_args = types.len() as u64;
632 let bb = builder.llbb();
633
634 unsafe {
636 llvm::LLVMRustPositionBuilderPastAllocas(&builder.llbuilder, builder.llfn());
637 }
638
639 let ty = cx.type_array(cx.type_ptr(), num_args);
640 let a1 = builder.direct_alloca(ty, Align::EIGHT, ".offload_baseptrs");
642 let a2 = builder.direct_alloca(ty, Align::EIGHT, ".offload_ptrs");
644 let ty2 = cx.type_array(cx.type_i64(), num_args);
646
647 let a4 = if has_dynamic {
648 let alloc = builder.direct_alloca(ty2, Align::EIGHT, ".offload_sizes");
649
650 builder.memcpy(
651 alloc,
652 Align::EIGHT,
653 offload_sizes,
654 Align::EIGHT,
655 cx.get_const_i64(8 * args.len() as u64),
656 MemFlags::empty(),
657 None,
658 );
659
660 alloc
661 } else {
662 offload_sizes
663 };
664
665 let a5 = builder.direct_alloca(tgt_kernel_decl, Align::EIGHT, "kernel_args");
667
668 unsafe {
670 llvm::LLVMPositionBuilderAtEnd(&builder.llbuilder, bb);
671 }
672
673 let mut vals = ::alloc::vec::Vec::new()vec![];
675 let mut geps = ::alloc::vec::Vec::new()vec![];
676 let i32_0 = cx.get_const_i32(0);
677 for &v in args {
678 let ty = cx.val_ty(v);
679 let ty_kind = cx.type_kind(ty);
680 let (base_val, gep_base) = match ty_kind {
681 TypeKind::Pointer => (v, v),
682 TypeKind::Half | TypeKind::Float | TypeKind::Double | TypeKind::Integer => {
683 let num_bits = scalar_width(cx, ty);
685
686 let bb = builder.llbb();
687 unsafe {
688 llvm::LLVMRustPositionBuilderPastAllocas(builder.llbuilder, builder.llfn());
689 }
690 let addr = builder.direct_alloca(cx.type_i64(), Align::EIGHT, "addr");
691 unsafe {
692 llvm::LLVMPositionBuilderAtEnd(builder.llbuilder, bb);
693 }
694
695 let cast = builder.bitcast(v, cx.type_ix(num_bits));
696 let value = builder.zext(cast, cx.type_i64());
697 builder.store(value, addr, Align::EIGHT);
698 (value, addr)
699 }
700 other => ::rustc_middle::util::bug::bug_fmt(format_args!("offload does not support {0:?}",
other))bug!("offload does not support {other:?}"),
701 };
702
703 let gep = builder.inbounds_gep(cx.type_f32(), gep_base, &[i32_0]);
704
705 vals.push(base_val);
706 geps.push(gep);
707 }
708
709 for i in 0..num_args {
710 let idx = cx.get_const_i32(i);
711 let gep1 = builder.inbounds_gep(ty, a1, &[i32_0, idx]);
712 builder.store(vals[i as usize], gep1, Align::EIGHT);
713 let gep2 = builder.inbounds_gep(ty, a2, &[i32_0, idx]);
714 builder.store(geps[i as usize], gep2, Align::EIGHT);
715
716 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(_)) {
717 let gep3 = builder.inbounds_gep(ty2, a4, &[i32_0, idx]);
718 let size_val = get_runtime_size(builder, args, i as usize, &metadata[i as usize]);
719 builder.store(size_val, gep3, Align::EIGHT);
720 }
721 }
722
723 fn get_geps<'ll, 'tcx>(
726 builder: &mut Builder<'_, 'll, 'tcx>,
727 ty: &'ll Type,
728 ty2: &'ll Type,
729 a1: &'ll Value,
730 a2: &'ll Value,
731 a4: &'ll Value,
732 is_dynamic: bool,
733 ) -> [&'ll Value; 3] {
734 let cx = builder.cx;
735 let i32_0 = cx.get_const_i32(0);
736
737 let gep1 = builder.inbounds_gep(ty, a1, &[i32_0, i32_0]);
738 let gep2 = builder.inbounds_gep(ty, a2, &[i32_0, i32_0]);
739 let gep3 = if is_dynamic { builder.inbounds_gep(ty2, a4, &[i32_0, i32_0]) } else { a4 };
740 [gep1, gep2, gep3]
741 }
742
743 fn generate_mapper_call<'ll, 'tcx>(
744 builder: &mut Builder<'_, 'll, 'tcx>,
745 geps: [&'ll Value; 3],
746 o_type: &'ll Value,
747 fn_to_call: &'ll Value,
748 fn_ty: &'ll Type,
749 num_args: u64,
750 s_ident_t: &'ll Value,
751 ) {
752 let cx = builder.cx;
753 let nullptr = cx.const_null(cx.type_ptr());
754 let i64_max = cx.get_const_i64(u64::MAX);
755 let num_args = cx.get_const_i32(num_args);
756 let args =
757 ::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];
758 builder.call(fn_ty, None, None, fn_to_call, &args, None, None);
759 }
760
761 let s_ident_t = offload_globals.ident_t_global;
763 let geps = get_geps(builder, ty, ty2, a1, a2, a4, has_dynamic);
764 generate_mapper_call(
765 builder,
766 geps,
767 memtransfer_begin,
768 begin_mapper_decl,
769 fn_ty,
770 num_args,
771 s_ident_t,
772 );
773 let values = KernelArgsTy::new(
774 &cx,
775 num_args,
776 memtransfer_kernel,
777 geps,
778 workgroup_dims,
779 thread_dims,
780 dyn_cache,
781 );
782
783 for (i, value) in values.iter().enumerate() {
786 let ptr = builder.inbounds_gep(tgt_kernel_decl, a5, &[i32_0, cx.get_const_i32(i as u64)]);
787 let name = std::ffi::CString::new(value.1).unwrap();
788 llvm::set_value_name(ptr, &name.as_bytes());
789
790 builder.store(value.2, ptr, value.0);
791 }
792
793 let device_id = builder.sext(device_id, cx.type_i64());
794 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];
795 builder.call(tgt_target_kernel_ty, None, None, tgt_decl, &args, None, None);
796 let geps = get_geps(builder, ty, ty2, a1, a2, a4, has_dynamic);
800 generate_mapper_call(
801 builder,
802 geps,
803 memtransfer_end,
804 end_mapper_decl,
805 fn_ty,
806 num_args,
807 s_ident_t,
808 );
809}