1use aliasable::boxed::AliasableBox;
2use anyhow::{anyhow, Result};
3use core::panic;
4use inkwell::attributes::{Attribute, AttributeLoc};
5use inkwell::basic_block::BasicBlock;
6use inkwell::builder::Builder;
7use inkwell::context::{AsContextRef, Context};
8use inkwell::debug_info::AsDIScope;
9use inkwell::debug_info::{DICompileUnit, DIFlags, DIFlagsConstants, DebugInfoBuilder};
10use inkwell::intrinsics::Intrinsic;
11use inkwell::module::{Linkage, Module};
12use inkwell::passes::PassBuilderOptions;
13use inkwell::targets::{FileType, InitializationConfig, Target, TargetMachine, TargetTriple};
14use inkwell::types::{
15 BasicMetadataTypeEnum, BasicType, BasicTypeEnum, FloatType, FunctionType, IntType, PointerType,
16};
17use inkwell::values::{
18 AsValueRef, BasicMetadataValueEnum, BasicValue, BasicValueEnum, CallSiteValue, FloatValue,
19 FunctionValue, GlobalValue, IntValue, PointerValue,
20};
21use inkwell::{
22 AddressSpace, AtomicOrdering, AtomicRMWBinOp, FloatPredicate, GlobalVisibility, IntPredicate,
23};
24use llvm_sys::core::{
25 LLVMBuildCall2, LLVMGetArgOperand, LLVMGetBasicBlockParent, LLVMGetGlobalParent,
26 LLVMGetInstructionParent, LLVMGetNamedFunction, LLVMGlobalGetValueType, LLVMIsMultithreaded,
27};
28use llvm_sys::prelude::{LLVMBuilderRef, LLVMValueRef};
29use pest::Span;
30use std::collections::HashMap;
31use std::ffi::CString;
32use std::iter::zip;
33use std::pin::Pin;
34use target_lexicon::Triple;
35
36use crate::ast::{Ast, AstKind, StringSpan};
37use crate::discretise::{DiscreteModel, Tensor, TensorBlock};
38use crate::enzyme::{
39 CConcreteType_DT_Anything, CConcreteType_DT_Double, CConcreteType_DT_Float,
40 CConcreteType_DT_Integer, CConcreteType_DT_Pointer, CDerivativeMode_DEM_ForwardMode,
41 CDerivativeMode_DEM_ReverseModeCombined, CFnTypeInfo, CreateEnzymeLogic, CreateTypeAnalysis,
42 DiffeGradientUtils, EnzymeCreateForwardDiff, EnzymeCreatePrimalAndGradient, EnzymeFreeTypeTree,
43 EnzymeGradientUtilsNewFromOriginal, EnzymeLogicRef, EnzymeMergeTypeTree, EnzymeNewTypeTreeCT,
44 EnzymeRegisterCallHandler, EnzymeTypeAnalysisRef, EnzymeTypeTreeOnlyEq, FreeEnzymeLogic,
45 FreeTypeAnalysis, GradientUtils, IntList, LLVMOpaqueContext, CDIFFE_TYPE_DFT_CONSTANT,
46 CDIFFE_TYPE_DFT_DUP_ARG, CDIFFE_TYPE_DFT_DUP_NONEED,
47};
48use crate::execution::compiler::CompilerOptions;
49use crate::execution::module::{
50 CodegenModule, CodegenModuleCompile, CodegenModuleEmit, CodegenModuleJit, CodegenModuleLink,
51};
52use crate::execution::object::ObjectModule;
53use crate::execution::scalar::RealType;
54use crate::execution::{DataLayout, Translation, TranslationFrom, TranslationTo};
55use lazy_static::lazy_static;
56use std::sync::Mutex;
57
58lazy_static! {
59 static ref my_mutex: Mutex<i32> = Mutex::new(0i32);
60}
61
62struct ImmovableLlvmModule {
63 codegen: Option<CodeGen<'static>>,
66 context: AliasableBox<Context>,
68 _pin: std::marker::PhantomPinned,
69}
70
71pub struct LlvmModule {
72 inner: Pin<Box<ImmovableLlvmModule>>,
73 machine: TargetMachine,
74 jit_module: Option<ObjectModule>,
75}
76
77unsafe impl Send for LlvmModule {}
78unsafe impl Sync for LlvmModule {}
79
80impl LlvmModule {
81 fn new(
82 triple: Option<Triple>,
83 model: &DiscreteModel,
84 threaded: bool,
85 real_type: RealType,
86 debug: bool,
87 constants_use_jit: bool,
88 ) -> Result<Self> {
89 let initialization_config = &InitializationConfig::default();
90 Target::initialize_all(initialization_config);
91 let host_triple = Triple::host();
92 let (triple_str, native) = match triple {
93 Some(ref triple) => (triple.to_string(), false),
94 None => (host_triple.to_string(), true),
95 };
96 let triple = TargetTriple::create(triple_str.as_str());
97 let target = Target::from_triple(&triple).unwrap();
98 let cpu = if native {
99 TargetMachine::get_host_cpu_name().to_string()
100 } else {
101 "generic".to_string()
102 };
103 let features = if native {
104 TargetMachine::get_host_cpu_features().to_string()
105 } else {
106 "".to_string()
107 };
108 let machine = target
109 .create_target_machine(
110 &triple,
111 cpu.as_str(),
112 features.as_str(),
113 inkwell::OptimizationLevel::Aggressive,
114 inkwell::targets::RelocMode::PIC,
115 inkwell::targets::CodeModel::Default,
116 )
117 .unwrap();
118
119 let context = AliasableBox::from_unique(Box::new(Context::create()));
120 let mut pinned = Self {
121 inner: Box::pin(ImmovableLlvmModule {
122 codegen: None,
123 context,
124 _pin: std::marker::PhantomPinned,
125 }),
126 machine,
127 jit_module: None,
128 };
129
130 let context_ref = pinned.inner.context.as_ref();
131 let real_type_llvm = match real_type {
132 RealType::F32 => context_ref.f32_type(),
133 RealType::F64 => context_ref.f64_type(),
134 };
135 let int_type_llvm = context_ref.i32_type();
136 let ptr_size_bits = pinned
137 .machine
138 .get_target_data()
139 .get_bit_size(&context_ref.ptr_type(AddressSpace::default()));
140 let real_size_bits = pinned
141 .machine
142 .get_target_data()
143 .get_bit_size(&real_type_llvm);
144 let int_size_bits = pinned
145 .machine
146 .get_target_data()
147 .get_bit_size(&int_type_llvm);
148 let codegen = CodeGen::new(
149 model,
150 context_ref,
151 real_type,
152 real_type_llvm,
153 int_type_llvm,
154 threaded,
155 ptr_size_bits,
156 real_size_bits,
157 int_size_bits,
158 debug,
159 constants_use_jit,
160 )?;
161 let codegen = unsafe { std::mem::transmute::<CodeGen<'_>, CodeGen<'static>>(codegen) };
162 unsafe { pinned.inner.as_mut().get_unchecked_mut().codegen = Some(codegen) };
163 Ok(pinned)
164 }
165
166 fn pre_autodiff_optimisation(&mut self) -> Result<()> {
167 let pass_options = PassBuilderOptions::create();
176 pass_options.set_loop_vectorization(false);
180 pass_options.set_loop_slp_vectorization(false);
181 pass_options.set_loop_unrolling(false);
182 let passes_base = "annotation2metadata,forceattrs,inferattrs,coro-early,function<eager-inv>(lower-expect,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;no-switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts>,early-cse<>),openmp-opt,ipsccp,called-value-propagation,globalopt,function(mem2reg),function<eager-inv>(instcombine,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts>),require<globals-aa>,function(invalidate<aa>),require<profile-summary>,cgscc(devirt<4>(inline<only-mandatory>,inline,function-attrs,openmp-opt-cgscc,function<eager-inv>(early-cse<memssa>,speculative-execution,jump-threading,correlated-propagation,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts>,instcombine,libcalls-shrinkwrap,tailcallelim,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts>,reassociate,require<opt-remark-emit>,loop-mssa(loop-instsimplify,loop-simplifycfg,licm<no-allowspeculation>,loop-rotate,licm<allowspeculation>,simple-loop-unswitch<no-nontrivial;trivial>),simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts>,instcombine,loop(loop-idiom,indvars,loop-deletion),vector-combine,mldst-motion<no-split-footer-bb>,gvn<>,sccp,bdce,instcombine,jump-threading,correlated-propagation,adce,memcpyopt,dse,loop-mssa(licm<allowspeculation>),coro-elide,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;hoist-common-insts;sink-common-insts>,instcombine),coro-split)),deadargelim,coro-cleanup,globalopt,globaldce,elim-avail-extern,rpo-function-attrs,recompute-globalsaa,function<eager-inv>(float2int,lower-constant-intrinsics,loop(loop-rotate,loop-deletion),loop-distribute,inject-tli-mappings,loop-load-elim,instcombine,simplifycfg<bonus-inst-threshold=1;forward-switch-cond;switch-range-to-icmp;switch-to-lookup;no-keep-loops;hoist-common-insts;sink-common-insts>,vector-combine,instcombine,transform-warning,instcombine,require<opt-remark-emit>,loop-mssa(licm<allowspeculation>),alignment-from-assumptions,loop-sink,instsimplify,div-rem-pairs,tailcallelim,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts>),globaldce,constmerge,cg-profile,rel-lookup-table-converter,function(annotation-remarks),verify";
195 let passes = if cfg!(feature = "inkwell-221") {
196 passes_base.replace("instcombine", "instcombine<no-verify-fixpoint>")
197 } else {
198 passes_base.to_string()
199 };
200 let (codegen, machine) = self.codegen_and_machine_mut();
201 codegen
202 .module()
203 .run_passes(&passes, machine, pass_options)
204 .map_err(|e| anyhow!("Failed to run passes: {:?}", e))
205
206 }
212
213 fn post_autodiff_optimisation(&mut self) -> Result<()> {
214 if let Some(barrier_func) = self.codegen_mut().module().get_function("barrier") {
216 let nolinline_kind_id = Attribute::get_named_enum_kind_id("noinline");
217 barrier_func.remove_enum_attribute(AttributeLoc::Function, nolinline_kind_id);
218 }
219
220 for f in self.codegen_mut().module.get_functions() {
222 if f.get_name().to_str().unwrap().starts_with("preprocess_") {
223 unsafe { f.delete() };
224 }
225 }
226
227 let passes = "default<O3>";
233 let (codegen, machine) = self.codegen_and_machine_mut();
234 codegen
235 .module()
236 .run_passes(passes, machine, PassBuilderOptions::create())
237 .map_err(|e| anyhow!("Failed to run passes: {:?}", e))?;
238
239 Ok(())
245 }
246
247 pub fn print(&self) {
248 self.codegen().module().print_to_stderr();
249 }
250 fn codegen_mut(&mut self) -> &mut CodeGen<'static> {
251 unsafe {
252 self.inner
253 .as_mut()
254 .get_unchecked_mut()
255 .codegen
256 .as_mut()
257 .unwrap()
258 }
259 }
260 fn codegen_and_machine_mut(&mut self) -> (&mut CodeGen<'static>, &TargetMachine) {
261 (
262 unsafe {
263 self.inner
264 .as_mut()
265 .get_unchecked_mut()
266 .codegen
267 .as_mut()
268 .unwrap()
269 },
270 &self.machine,
271 )
272 }
273
274 fn codegen(&self) -> &CodeGen<'static> {
275 self.inner.as_ref().get_ref().codegen.as_ref().unwrap()
276 }
277
278 pub fn to_dynamic_library(self, output_path: impl Into<std::path::PathBuf>) -> Result<()> {
279 use std::fs;
280 use std::process::Command;
281
282 let output_path = output_path.into();
283 let object_buffer = self.to_object()?;
284
285 let temp_dir = std::env::temp_dir();
287 let obj_path = temp_dir.join("diffsl_temp_object.o");
288 fs::write(&obj_path, object_buffer)
289 .map_err(|e| anyhow!("Failed to write temporary object file: {}", e))?;
290
291 let lld = option_env!("DIFFSL_LLVM_LLD")
292 .ok_or_else(|| anyhow!("DIFFSL_LLVM_LLD not set by build script"))?;
293 let linker_name = std::path::Path::new(lld)
294 .file_name()
295 .and_then(|name| name.to_str())
296 .unwrap_or(lld);
297 let is_clang_driver = linker_name.starts_with("clang");
298
299 let mut command = Command::new(lld);
300 if cfg!(target_os = "windows") {
301 if is_clang_driver {
302 command.arg("-shared");
303 command.arg("-o");
304 command.arg(&output_path);
305 command.arg(&obj_path);
306 } else {
307 command.arg("-flavor").arg("link");
308 command.arg("/DLL");
309 command.arg(format!("/OUT:{}", output_path.display()));
310 command.arg(&obj_path);
311 }
312 } else if cfg!(target_os = "macos") {
313 if !lld.ends_with("ld64.lld") && !is_clang_driver {
314 command.arg("-flavor").arg("darwin");
315 }
316 let arch = if cfg!(target_arch = "aarch64") {
317 "arm64"
318 } else if cfg!(target_arch = "x86_64") {
319 "x86_64"
320 } else {
321 return Err(anyhow!("Unsupported macOS architecture for lld invocation"));
322 };
323 let deployment_target =
324 std::env::var("MACOSX_DEPLOYMENT_TARGET").unwrap_or_else(|_| "11.0".to_string());
325 if is_clang_driver {
326 command.arg("-dynamiclib");
327 command.arg("-arch");
328 command.arg(arch);
329 command.arg(format!("-mmacosx-version-min={deployment_target}"));
330 command.arg("-o");
331 command.arg(&output_path);
332 command.arg(&obj_path);
333 } else {
334 command.arg("-arch");
335 command.arg(arch);
336 command.arg("-platform_version");
337 command.arg("macos");
338 command.arg(&deployment_target);
339 command.arg(&deployment_target);
340 command.arg("-dylib");
341 command.arg("-o");
342 command.arg(&output_path);
343 command.arg(&obj_path);
344 }
345 } else {
346 if is_clang_driver {
347 command.arg("-shared");
348 command.arg("-o");
349 command.arg(&output_path);
350 command.arg(&obj_path);
351 } else {
352 command.arg("-flavor").arg("gnu");
353 command.arg("-shared");
354 command.arg("-o");
355 command.arg(&output_path);
356 command.arg(&obj_path);
357 }
358 }
359
360 let status = command
361 .status()
362 .map_err(|e| anyhow!("Failed to invoke lld: {}", e))?;
363 if !status.success() {
364 return Err(anyhow!(
365 "Dynamic library link failed with status: {}",
366 status
367 ));
368 }
369
370 let _ = fs::remove_file(&obj_path);
371 Ok(())
372 }
373}
374
375impl CodegenModule for LlvmModule {}
376
377impl CodegenModuleCompile for LlvmModule {
378 fn from_discrete_model(
379 model: &DiscreteModel,
380 options: CompilerOptions,
381 triple: Option<Triple>,
382 real_type: RealType,
383 code: Option<&str>,
384 ) -> Result<Self> {
385 let thread_dim = options.mode.thread_dim(model.state().nnz());
386 let threaded = thread_dim > 1;
387 if (unsafe { LLVMIsMultithreaded() } <= 0) {
388 return Err(anyhow!(
389 "LLVM is not compiled with multithreading support, but this codegen module requires it."
390 ));
391 }
392
393 let constants_use_jit = options.constants_use_jit;
394 let mut module = Self::new(
395 triple,
396 model,
397 threaded,
398 real_type,
399 options.debug,
400 constants_use_jit,
401 )?;
402
403 let set_u0 = module.codegen_mut().compile_set_u0(model, code)?;
404 let calc_stop = module.codegen_mut().compile_calc_stop(model, false, code)?;
405 let calc_stop_full = module.codegen_mut().compile_calc_stop(model, true, code)?;
406 let reset = module.codegen_mut().compile_reset(model, false, code)?;
407 let reset_full = module.codegen_mut().compile_reset(model, true, code)?;
408 let rhs = module.codegen_mut().compile_rhs(model, false, code)?;
409 let rhs_full = module.codegen_mut().compile_rhs(model, true, code)?;
410 let mass = module.codegen_mut().compile_mass(model, code)?;
411 let calc_out = module.codegen_mut().compile_calc_out(model, false, code)?;
412 let calc_out_full = module.codegen_mut().compile_calc_out(model, true, code)?;
413 let _set_id = module.codegen_mut().compile_set_id(model)?;
414 let _get_dims = module.codegen_mut().compile_get_dims(model)?;
415 let set_inputs = module.codegen_mut().compile_inputs(model, false)?;
416 let _get_inputs = module.codegen_mut().compile_inputs(model, true)?;
417 let _set_constants =
418 module
419 .codegen_mut()
420 .compile_set_constants(model, code, constants_use_jit)?;
421 let tensor_info = module
422 .codegen()
423 .layout
424 .tensors()
425 .map(|(name, is_constant)| (name.to_string(), is_constant))
426 .collect::<Vec<_>>();
427 for (tensor, is_constant) in tensor_info {
428 if is_constant {
429 module
430 .codegen_mut()
431 .compile_get_constant(model, tensor.as_str())?;
432 } else {
433 module
434 .codegen_mut()
435 .compile_get_tensor(model, tensor.as_str())?;
436 }
437 }
438
439 module.pre_autodiff_optimisation()?;
440
441 module.codegen_mut().compile_gradient(
442 set_u0,
443 &[
444 CompileGradientArgType::DupNoNeed,
445 CompileGradientArgType::DupNoNeed,
446 CompileGradientArgType::Const,
447 CompileGradientArgType::Const,
448 ],
449 CompileMode::Forward,
450 "set_u0_grad",
451 )?;
452
453 module.codegen_mut().compile_gradient(
454 rhs,
455 &[
456 CompileGradientArgType::Const,
457 CompileGradientArgType::DupNoNeed,
458 CompileGradientArgType::DupNoNeed,
459 CompileGradientArgType::DupNoNeed,
460 CompileGradientArgType::Const,
461 CompileGradientArgType::Const,
462 ],
463 CompileMode::Forward,
464 "rhs_grad",
465 )?;
466
467 module.codegen_mut().compile_gradient(
468 reset,
469 &[
470 CompileGradientArgType::Const,
471 CompileGradientArgType::DupNoNeed,
472 CompileGradientArgType::DupNoNeed,
473 CompileGradientArgType::DupNoNeed,
474 CompileGradientArgType::Const,
475 CompileGradientArgType::Const,
476 ],
477 CompileMode::Forward,
478 "reset_grad",
479 )?;
480
481 module.codegen_mut().compile_gradient(
482 calc_stop,
483 &[
484 CompileGradientArgType::Const,
485 CompileGradientArgType::DupNoNeed,
486 CompileGradientArgType::DupNoNeed,
487 CompileGradientArgType::DupNoNeed,
488 CompileGradientArgType::Const,
489 CompileGradientArgType::Const,
490 ],
491 CompileMode::Forward,
492 "calc_stop_grad",
493 )?;
494
495 module.codegen_mut().compile_gradient(
496 calc_out,
497 &[
498 CompileGradientArgType::Const,
499 CompileGradientArgType::DupNoNeed,
500 CompileGradientArgType::DupNoNeed,
501 CompileGradientArgType::DupNoNeed,
502 CompileGradientArgType::Const,
503 CompileGradientArgType::Const,
504 ],
505 CompileMode::Forward,
506 "calc_out_grad",
507 )?;
508 module.codegen_mut().compile_gradient(
509 set_inputs,
510 &[
511 CompileGradientArgType::DupNoNeed,
512 CompileGradientArgType::DupNoNeed,
513 CompileGradientArgType::Const,
514 ],
515 CompileMode::Forward,
516 "set_inputs_grad",
517 )?;
518
519 module.codegen_mut().compile_gradient(
520 set_u0,
521 &[
522 CompileGradientArgType::DupNoNeed,
523 CompileGradientArgType::DupNoNeed,
524 CompileGradientArgType::Const,
525 CompileGradientArgType::Const,
526 ],
527 CompileMode::Reverse,
528 "set_u0_rgrad",
529 )?;
530
531 module.codegen_mut().compile_gradient(
532 mass,
533 &[
534 CompileGradientArgType::Const,
535 CompileGradientArgType::DupNoNeed,
536 CompileGradientArgType::DupNoNeed,
537 CompileGradientArgType::DupNoNeed,
538 CompileGradientArgType::Const,
539 CompileGradientArgType::Const,
540 ],
541 CompileMode::Reverse,
542 "mass_rgrad",
543 )?;
544
545 module.codegen_mut().compile_gradient(
546 rhs,
547 &[
548 CompileGradientArgType::Const,
549 CompileGradientArgType::DupNoNeed,
550 CompileGradientArgType::DupNoNeed,
551 CompileGradientArgType::DupNoNeed,
552 CompileGradientArgType::Const,
553 CompileGradientArgType::Const,
554 ],
555 CompileMode::Reverse,
556 "rhs_rgrad",
557 )?;
558 module.codegen_mut().compile_gradient(
559 reset,
560 &[
561 CompileGradientArgType::Const,
562 CompileGradientArgType::DupNoNeed,
563 CompileGradientArgType::DupNoNeed,
564 CompileGradientArgType::DupNoNeed,
565 CompileGradientArgType::Const,
566 CompileGradientArgType::Const,
567 ],
568 CompileMode::Reverse,
569 "reset_rgrad",
570 )?;
571 module.codegen_mut().compile_gradient(
572 calc_stop,
573 &[
574 CompileGradientArgType::Const,
575 CompileGradientArgType::DupNoNeed,
576 CompileGradientArgType::DupNoNeed,
577 CompileGradientArgType::DupNoNeed,
578 CompileGradientArgType::Const,
579 CompileGradientArgType::Const,
580 ],
581 CompileMode::Reverse,
582 "calc_stop_rgrad",
583 )?;
584 module.codegen_mut().compile_gradient(
585 calc_out,
586 &[
587 CompileGradientArgType::Const,
588 CompileGradientArgType::DupNoNeed,
589 CompileGradientArgType::DupNoNeed,
590 CompileGradientArgType::DupNoNeed,
591 CompileGradientArgType::Const,
592 CompileGradientArgType::Const,
593 ],
594 CompileMode::Reverse,
595 "calc_out_rgrad",
596 )?;
597
598 module.codegen_mut().compile_gradient(
599 set_inputs,
600 &[
601 CompileGradientArgType::DupNoNeed,
602 CompileGradientArgType::DupNoNeed,
603 CompileGradientArgType::Const,
604 ],
605 CompileMode::Reverse,
606 "set_inputs_rgrad",
607 )?;
608
609 module.codegen_mut().compile_gradient(
610 rhs_full,
611 &[
612 CompileGradientArgType::Const,
613 CompileGradientArgType::Const,
614 CompileGradientArgType::DupNoNeed,
615 CompileGradientArgType::DupNoNeed,
616 CompileGradientArgType::Const,
617 CompileGradientArgType::Const,
618 ],
619 CompileMode::ForwardSens,
620 "rhs_sgrad",
621 )?;
622
623 module.codegen_mut().compile_gradient(
624 reset_full,
625 &[
626 CompileGradientArgType::Const,
627 CompileGradientArgType::Const,
628 CompileGradientArgType::DupNoNeed,
629 CompileGradientArgType::DupNoNeed,
630 CompileGradientArgType::Const,
631 CompileGradientArgType::Const,
632 ],
633 CompileMode::ForwardSens,
634 "reset_sgrad",
635 )?;
636
637 module.codegen_mut().compile_gradient(
638 calc_stop_full,
639 &[
640 CompileGradientArgType::Const,
641 CompileGradientArgType::Const,
642 CompileGradientArgType::DupNoNeed,
643 CompileGradientArgType::DupNoNeed,
644 CompileGradientArgType::Const,
645 CompileGradientArgType::Const,
646 ],
647 CompileMode::ForwardSens,
648 "calc_stop_sgrad",
649 )?;
650
651 module.codegen_mut().compile_gradient(
652 set_u0,
653 &[
654 CompileGradientArgType::DupNoNeed,
655 CompileGradientArgType::DupNoNeed,
656 CompileGradientArgType::Const,
657 CompileGradientArgType::Const,
658 ],
659 CompileMode::ForwardSens,
660 "set_u0_sgrad",
661 )?;
662
663 module.codegen_mut().compile_gradient(
664 calc_out_full,
665 &[
666 CompileGradientArgType::Const,
667 CompileGradientArgType::Const,
668 CompileGradientArgType::DupNoNeed,
669 CompileGradientArgType::DupNoNeed,
670 CompileGradientArgType::Const,
671 CompileGradientArgType::Const,
672 ],
673 CompileMode::ForwardSens,
674 "calc_out_sgrad",
675 )?;
676 module.codegen_mut().compile_gradient(
677 calc_out_full,
678 &[
679 CompileGradientArgType::Const,
680 CompileGradientArgType::Const,
681 CompileGradientArgType::DupNoNeed,
682 CompileGradientArgType::DupNoNeed,
683 CompileGradientArgType::Const,
684 CompileGradientArgType::Const,
685 ],
686 CompileMode::ReverseSens,
687 "calc_out_srgrad",
688 )?;
689
690 module.codegen_mut().compile_gradient(
691 rhs_full,
692 &[
693 CompileGradientArgType::Const,
694 CompileGradientArgType::Const,
695 CompileGradientArgType::DupNoNeed,
696 CompileGradientArgType::DupNoNeed,
697 CompileGradientArgType::Const,
698 CompileGradientArgType::Const,
699 ],
700 CompileMode::ReverseSens,
701 "rhs_srgrad",
702 )?;
703
704 module.codegen_mut().compile_gradient(
705 reset_full,
706 &[
707 CompileGradientArgType::Const,
708 CompileGradientArgType::Const,
709 CompileGradientArgType::DupNoNeed,
710 CompileGradientArgType::DupNoNeed,
711 CompileGradientArgType::Const,
712 CompileGradientArgType::Const,
713 ],
714 CompileMode::ReverseSens,
715 "reset_srgrad",
716 )?;
717
718 module.codegen_mut().compile_gradient(
719 calc_stop_full,
720 &[
721 CompileGradientArgType::Const,
722 CompileGradientArgType::Const,
723 CompileGradientArgType::DupNoNeed,
724 CompileGradientArgType::DupNoNeed,
725 CompileGradientArgType::Const,
726 CompileGradientArgType::Const,
727 ],
728 CompileMode::ReverseSens,
729 "calc_stop_srgrad",
730 )?;
731
732 module.post_autodiff_optimisation()?;
733 Ok(module)
734 }
735}
736
737impl CodegenModuleEmit for LlvmModule {
738 fn to_object(&self) -> Result<Vec<u8>> {
739 let module = self.codegen().module();
740 let buffer = self
742 .machine
743 .write_to_memory_buffer(module, FileType::Object)
744 .unwrap()
745 .as_slice()
746 .to_vec();
747 Ok(buffer)
748 }
749}
750impl CodegenModuleJit for LlvmModule {
751 fn jit(&mut self) -> Result<HashMap<String, *const u8>> {
752 let object = self.to_object()?;
753 let mut jit_module = ObjectModule::from_object(&object)?;
754 let symbols = jit_module.jit()?;
755 self.jit_module = Some(jit_module);
756 Ok(symbols)
757 }
758}
759
760struct Globals<'ctx> {
761 indices: Option<GlobalValue<'ctx>>,
762 constants: Option<GlobalValue<'ctx>>,
763 thread_counter: Option<GlobalValue<'ctx>>,
764 model_index: GlobalValue<'ctx>,
765}
766
767impl<'ctx> Globals<'ctx> {
768 fn new(
769 layout: &DataLayout,
770 module: &Module<'ctx>,
771 int_type: IntType<'ctx>,
772 real_type: FloatType<'ctx>,
773 threaded: bool,
774 constants_use_jit: bool,
775 ) -> Self {
776 let thread_counter = if threaded {
777 let tc = module.add_global(
778 int_type,
779 Some(AddressSpace::default()),
780 "enzyme_const_thread_counter",
781 );
782 let tc_value = int_type.const_zero();
789 tc.set_visibility(GlobalVisibility::Hidden);
790 tc.set_initializer(&tc_value.as_basic_value_enum());
791 Some(tc)
792 } else {
793 None
794 };
795 let constants = if layout.constants().is_empty() {
796 None
797 } else {
798 let constants_array_type =
799 real_type.array_type(u32::try_from(layout.constants().len()).unwrap());
800 let constants = module.add_global(
801 constants_array_type,
802 Some(AddressSpace::default()),
803 "enzyme_const_constants",
804 );
805 constants.set_visibility(GlobalVisibility::Hidden);
806 constants.set_constant(!constants_use_jit);
807 let constants_array_values = layout
808 .constants()
809 .iter()
810 .map(|&value| real_type.const_float(value))
811 .collect::<Vec<_>>();
812 let constants_value = real_type.const_array(constants_array_values.as_slice());
813 constants.set_initializer(&constants_value);
814 Some(constants)
815 };
816 let indices = if layout.indices().is_empty() {
817 None
818 } else {
819 let indices_array_type =
820 int_type.array_type(u32::try_from(layout.indices().len()).unwrap());
821 let indices_array_values = layout
822 .indices()
823 .iter()
824 .map(|&i| int_type.const_int(i as u64, true))
825 .collect::<Vec<IntValue>>();
826 let indices_value = int_type.const_array(indices_array_values.as_slice());
827 let indices = module.add_global(
828 indices_array_type,
829 Some(AddressSpace::default()),
830 "enzyme_const_indices",
831 );
832 indices.set_constant(true);
833 indices.set_visibility(GlobalVisibility::Hidden);
834 indices.set_initializer(&indices_value);
835 Some(indices)
836 };
837 let model_index = module.add_global(
838 int_type,
839 Some(AddressSpace::default()),
840 "enzyme_const_model_index",
841 );
842 model_index.set_visibility(GlobalVisibility::Hidden);
843 model_index.set_constant(false);
844 model_index.set_initializer(&int_type.const_zero());
845 Self {
846 indices,
847 thread_counter,
848 constants,
849 model_index,
850 }
851 }
852}
853
854pub enum CompileGradientArgType {
855 Const,
856 Dup,
857 DupNoNeed,
858}
859
860pub enum CompileMode {
861 Forward,
862 ForwardSens,
863 Reverse,
864 ReverseSens,
865}
866
867pub struct CodeGen<'ctx> {
868 context: &'ctx inkwell::context::Context,
869 module: Module<'ctx>,
870 builder: Builder<'ctx>,
871 dibuilder: Option<DebugInfoBuilder<'ctx>>,
872 compile_unit: Option<DICompileUnit<'ctx>>,
873 variables: HashMap<String, PointerValue<'ctx>>,
874 functions: HashMap<String, FunctionValue<'ctx>>,
875 fn_value_opt: Option<FunctionValue<'ctx>>,
876 tensor_ptr_opt: Option<PointerValue<'ctx>>,
877 diffsl_real_type: RealType,
878 real_type: FloatType<'ctx>,
879 real_ptr_type: PointerType<'ctx>,
880 int_type: IntType<'ctx>,
881 int_ptr_type: PointerType<'ctx>,
882 ptr_size_bits: u64,
883 int_size_bits: u64,
884 real_size_bits: u64,
885 layout: DataLayout,
886 globals: Globals<'ctx>,
887 threaded: bool,
888}
889
890unsafe extern "C" fn fwd_handler(
891 _builder: LLVMBuilderRef,
892 _call_instruction: LLVMValueRef,
893 _gutils: *mut GradientUtils,
894 _dcall: *mut LLVMValueRef,
895 _normal_return: *mut LLVMValueRef,
896 _shadow_return: *mut LLVMValueRef,
897) -> u8 {
898 1
899}
900
901unsafe extern "C" fn rev_handler(
902 builder: LLVMBuilderRef,
903 call_instruction: LLVMValueRef,
904 gutils: *mut DiffeGradientUtils,
905 _tape: LLVMValueRef,
906) {
907 let call_block = LLVMGetInstructionParent(call_instruction);
908 let call_function = LLVMGetBasicBlockParent(call_block);
909 let module = LLVMGetGlobalParent(call_function);
910 let name_c_str = CString::new("barrier_grad").unwrap();
911 let barrier_func = LLVMGetNamedFunction(module, name_c_str.as_ptr());
912 let barrier_func_type = LLVMGlobalGetValueType(barrier_func);
913 let barrier_num = LLVMGetArgOperand(call_instruction, 0);
914 let total_barriers = LLVMGetArgOperand(call_instruction, 1);
915 let thread_count = LLVMGetArgOperand(call_instruction, 2);
916 let barrier_num = EnzymeGradientUtilsNewFromOriginal(gutils as *mut GradientUtils, barrier_num);
917 let total_barriers =
918 EnzymeGradientUtilsNewFromOriginal(gutils as *mut GradientUtils, total_barriers);
919 let thread_count =
920 EnzymeGradientUtilsNewFromOriginal(gutils as *mut GradientUtils, thread_count);
921 let mut args = [barrier_num, total_barriers, thread_count];
922 let name_c_str = CString::new("").unwrap();
923 LLVMBuildCall2(
924 builder,
925 barrier_func_type,
926 barrier_func,
927 args.as_mut_ptr(),
928 args.len() as u32,
929 name_c_str.as_ptr(),
930 );
931}
932
933#[allow(dead_code)]
934enum PrintValue<'ctx> {
935 Real(FloatValue<'ctx>),
936 Int(IntValue<'ctx>),
937}
938
939impl<'ctx> CodeGen<'ctx> {
940 #[allow(clippy::too_many_arguments)]
941 pub fn new(
942 model: &DiscreteModel,
943 context: &'ctx inkwell::context::Context,
944 diffsl_real_type: RealType,
945 real_type: FloatType<'ctx>,
946 int_type: IntType<'ctx>,
947 threaded: bool,
948 ptr_size_bits: u64,
949 real_size_bits: u64,
950 int_size_bits: u64,
951 debug: bool,
952 constants_use_jit: bool,
953 ) -> Result<Self> {
954 let builder = context.create_builder();
955 let layout = DataLayout::new(model)?;
956 let module = context.create_module(model.name());
957 let (dibuilder, compile_unit) = if debug {
958 let (dib, dic) = module.create_debug_info_builder(
959 true,
960 inkwell::debug_info::DWARFSourceLanguage::C,
961 model.name(),
962 ".",
963 "diffsl compiler",
964 true,
965 "",
966 0,
967 "",
968 inkwell::debug_info::DWARFEmissionKind::Full,
969 0,
970 false,
971 false,
972 "",
973 "",
974 );
975 (Some(dib), Some(dic))
976 } else {
977 (None, None)
978 };
979 let globals = Globals::new(
980 &layout,
981 &module,
982 int_type,
983 real_type,
984 threaded,
985 constants_use_jit,
986 );
987 let real_ptr_type = Self::pointer_type(context, real_type.into());
988 let int_ptr_type = Self::pointer_type(context, int_type.into());
989 let mut ret = Self {
990 context,
991 module,
992 builder,
993 dibuilder,
994 compile_unit,
995 real_type,
996 real_ptr_type,
997 variables: HashMap::new(),
998 functions: HashMap::new(),
999 fn_value_opt: None,
1000 tensor_ptr_opt: None,
1001 layout,
1002 diffsl_real_type,
1003 int_type,
1004 int_ptr_type,
1005 globals,
1006 threaded,
1007 ptr_size_bits,
1008 real_size_bits,
1009 int_size_bits,
1010 };
1011 if threaded {
1012 ret.compile_barrier_init()?;
1013 ret.compile_barrier()?;
1014 ret.compile_barrier_grad()?;
1015 }
1018 Ok(ret)
1019 }
1020
1021 fn start_function(
1022 &mut self,
1023 function: FunctionValue<'ctx>,
1024 _code: Option<&str>,
1025 ) -> BasicBlock<'ctx> {
1026 let basic_block = self.context.append_basic_block(function, "entry");
1027 self.fn_value_opt = Some(function);
1028 self.builder.position_at_end(basic_block);
1029
1030 if let Some(dibuilder) = &self.dibuilder {
1031 let scope = self
1032 .fn_value_opt
1033 .unwrap()
1034 .get_subprogram()
1035 .unwrap()
1036 .as_debug_info_scope();
1037 let loc = dibuilder.create_debug_location(self.context, 0, 0, scope, None);
1038 self.builder.set_current_debug_location(loc);
1039 }
1040 basic_block
1041 }
1042
1043 fn add_function(
1044 &mut self,
1045 name: &str,
1046 arg_names: &[&str],
1047 arg_types: &[BasicMetadataTypeEnum<'ctx>],
1048 linkage: Option<Linkage>,
1049 is_real_return: bool,
1050 ) -> FunctionValue<'ctx> {
1051 let function_type = if is_real_return {
1052 self.real_type.fn_type(arg_types, false)
1053 } else {
1054 self.context.void_type().fn_type(arg_types, false)
1055 };
1056 let function = self.module.add_function(name, function_type, linkage);
1057 if let (Some(dibuilder), Some(compile_unit)) = (&self.dibuilder, &self.compile_unit) {
1058 let ditypes = arg_names
1059 .iter()
1060 .zip(arg_types.iter())
1061 .map(|(&name, &ty)| {
1062 let size_in_bits = if ty.is_float_type() {
1063 self.real_size_bits
1064 } else if ty.is_int_type() {
1065 self.int_size_bits
1066 } else if ty.is_pointer_type() {
1067 self.ptr_size_bits
1068 } else {
1069 unreachable!("Unsupported argument type for debug info")
1070 };
1071 dibuilder
1072 .create_basic_type(
1073 name,
1074 size_in_bits,
1075 0x00,
1076 <DIFlags as DIFlagsConstants>::PUBLIC,
1077 )
1078 .unwrap()
1079 .as_type()
1080 })
1081 .collect::<Vec<_>>();
1082 let subroutine_type = dibuilder.create_subroutine_type(
1083 compile_unit.get_file(),
1084 None,
1085 &ditypes,
1086 <DIFlags as DIFlagsConstants>::PUBLIC,
1087 );
1088 let func_scope = dibuilder.create_function(
1089 compile_unit.as_debug_info_scope(),
1090 name,
1091 None,
1092 compile_unit.get_file(),
1093 0,
1094 subroutine_type,
1095 true,
1096 true,
1097 0,
1098 <DIFlags as DIFlagsConstants>::PUBLIC,
1099 true,
1100 );
1101 function.set_subprogram(func_scope);
1102 }
1103 self.functions.insert(name.to_owned(), function);
1104 function
1105 }
1106
1107 #[allow(dead_code)]
1108 fn compile_print_value(
1109 &mut self,
1110 name: &str,
1111 value: PrintValue<'ctx>,
1112 ) -> Result<CallSiteValue<'_>> {
1113 let printf = match self.module.get_function("printf") {
1115 Some(f) => f,
1116 None => self.add_function(
1118 "printf",
1119 &["format"],
1120 &[self.int_ptr_type.into()],
1121 Some(Linkage::External),
1122 false,
1123 ),
1124 };
1125 let (format_str, format_str_name) = match value {
1126 PrintValue::Real(_) => (format!("{name}: %f\n"), format!("real_format_{name}")),
1127 PrintValue::Int(_) => (format!("{name}: %d\n"), format!("int_format_{name}")),
1128 };
1129 let format_str = CString::new(format_str).unwrap();
1131 let format_str_global = match self.module.get_global(format_str_name.as_str()) {
1133 Some(g) => g,
1134 None => {
1135 let format_str = self.context.const_string(format_str.as_bytes(), true);
1136 let fmt_str =
1137 self.module
1138 .add_global(format_str.get_type(), None, format_str_name.as_str());
1139 fmt_str.set_initializer(&format_str);
1140 fmt_str.set_visibility(GlobalVisibility::Hidden);
1141 fmt_str
1142 }
1143 };
1144 let format_str_ptr = self.builder.build_pointer_cast(
1146 format_str_global.as_pointer_value(),
1147 self.int_ptr_type,
1148 "format_str_ptr",
1149 )?;
1150 let value: BasicMetadataValueEnum = match value {
1151 PrintValue::Real(v) => v.into(),
1152 PrintValue::Int(v) => v.into(),
1153 };
1154 self.builder
1155 .build_call(printf, &[format_str_ptr.into(), value], "printf_call")
1156 .map_err(|e| anyhow!("Error building call to printf: {}", e))
1157 }
1158
1159 fn compile_set_constants(
1160 &mut self,
1161 model: &DiscreteModel,
1162 code: Option<&str>,
1163 constants_use_jit: bool,
1164 ) -> Result<FunctionValue<'ctx>> {
1165 self.clear();
1166 let fn_arg_names = &["thread_id", "thread_dim"];
1167 let function = self.add_function(
1168 "set_constants",
1169 fn_arg_names,
1170 &[self.int_type.into(), self.int_type.into()],
1171 None,
1172 false,
1173 );
1174 let _basic_block = self.start_function(function, code);
1175
1176 for (i, arg) in function.get_param_iter().enumerate() {
1177 let name = fn_arg_names[i];
1178 let alloca = self.function_arg_alloca(name, arg);
1179 self.insert_param(name, alloca);
1180 }
1181
1182 self.insert_indices();
1183 self.insert_constants(model);
1184
1185 if constants_use_jit {
1186 let mut nbarriers = 0;
1187 let total_barriers = (model.constant_defns().len()) as u64;
1188 let total_barriers_val = self.int_type.const_int(total_barriers, false);
1189 #[allow(clippy::explicit_counter_loop)]
1190 for a in model.constant_defns() {
1191 self.jit_compile_tensor(a, Some(*self.get_var(a)), code)?;
1192 let barrier_num = self.int_type.const_int(nbarriers + 1, false);
1193 self.jit_compile_call_barrier(barrier_num, total_barriers_val);
1194 nbarriers += 1;
1195 }
1196 }
1197
1198 self.builder.build_return(None)?;
1199
1200 if function.verify(true) {
1201 Ok(function)
1202 } else {
1203 function.print_to_stderr();
1204 self.module.print_to_stderr();
1205 unsafe {
1206 function.delete();
1207 }
1208 Err(anyhow!("Invalid generated function."))
1209 }
1210 }
1211
1212 fn compile_barrier_init(&mut self) -> Result<FunctionValue<'ctx>> {
1213 self.clear();
1214 let function = self.add_function("barrier_init", &[], &[], None, false);
1215 let _entry_block = self.start_function(function, None);
1216
1217 let thread_counter = self.globals.thread_counter.unwrap().as_pointer_value();
1218 self.builder
1219 .build_store(thread_counter, self.int_type.const_zero())?;
1220
1221 self.builder.build_return(None)?;
1222
1223 if function.verify(true) {
1224 Ok(function)
1225 } else {
1226 function.print_to_stderr();
1227 unsafe {
1228 function.delete();
1229 }
1230 Err(anyhow!("Invalid generated function."))
1231 }
1232 }
1233
1234 fn compile_barrier(&mut self) -> Result<FunctionValue<'ctx>> {
1235 self.clear();
1236 let function = self.add_function(
1237 "barrier",
1238 &["barrier_num", "total_barriers", "thread_count"],
1239 &[
1240 self.int_type.into(),
1241 self.int_type.into(),
1242 self.int_type.into(),
1243 ],
1244 None,
1245 false,
1246 );
1247 let nolinline_kind_id = Attribute::get_named_enum_kind_id("noinline");
1248 let noinline = self.context.create_enum_attribute(nolinline_kind_id, 0);
1249 function.add_attribute(AttributeLoc::Function, noinline);
1250
1251 let _entry_block = self.start_function(function, None);
1252 let increment_block = self.context.append_basic_block(function, "increment");
1253 let wait_loop_block = self.context.append_basic_block(function, "wait_loop");
1254 let barrier_done_block = self.context.append_basic_block(function, "barrier_done");
1255
1256 let thread_counter = self.globals.thread_counter.unwrap().as_pointer_value();
1257 let barrier_num = function.get_nth_param(0).unwrap().into_int_value();
1258 let total_barriers = function.get_nth_param(1).unwrap().into_int_value();
1259 let thread_count = function.get_nth_param(2).unwrap().into_int_value();
1260
1261 let nbarrier_equals_total_barriers = self
1262 .builder
1263 .build_int_compare(
1264 IntPredicate::EQ,
1265 barrier_num,
1266 total_barriers,
1267 "nbarrier_equals_total_barriers",
1268 )
1269 .unwrap();
1270 self.builder.build_conditional_branch(
1272 nbarrier_equals_total_barriers,
1273 barrier_done_block,
1274 increment_block,
1275 )?;
1276 self.builder.position_at_end(increment_block);
1277
1278 let barrier_num_times_thread_count = self
1279 .builder
1280 .build_int_mul(barrier_num, thread_count, "barrier_num_times_thread_count")
1281 .unwrap();
1282
1283 let i32_type = self.context.i32_type();
1285 let one = i32_type.const_int(1, false);
1286 self.builder.build_atomicrmw(
1287 AtomicRMWBinOp::Add,
1288 thread_counter,
1289 one,
1290 AtomicOrdering::Monotonic,
1291 )?;
1292
1293 self.builder.build_unconditional_branch(wait_loop_block)?;
1295 self.builder.position_at_end(wait_loop_block);
1296
1297 let current_value = self
1298 .builder
1299 .build_load(i32_type, thread_counter, "current_value")?
1300 .into_int_value();
1301
1302 current_value
1303 .as_instruction_value()
1304 .unwrap()
1305 .set_atomic_ordering(AtomicOrdering::Monotonic)
1306 .map_err(|e| anyhow!("Error setting atomic ordering: {:?}", e))?;
1307
1308 let all_threads_done = self.builder.build_int_compare(
1309 IntPredicate::UGE,
1310 current_value,
1311 barrier_num_times_thread_count,
1312 "all_threads_done",
1313 )?;
1314
1315 self.builder.build_conditional_branch(
1316 all_threads_done,
1317 barrier_done_block,
1318 wait_loop_block,
1319 )?;
1320 self.builder.position_at_end(barrier_done_block);
1321
1322 self.builder.build_return(None)?;
1323
1324 if function.verify(true) {
1325 Ok(function)
1326 } else {
1327 function.print_to_stderr();
1328 unsafe {
1329 function.delete();
1330 }
1331 Err(anyhow!("Invalid generated function."))
1332 }
1333 }
1334
1335 fn compile_barrier_grad(&mut self) -> Result<FunctionValue<'ctx>> {
1336 self.clear();
1337 let function = self.add_function(
1338 "barrier_grad",
1339 &["barrier_num", "total_barriers", "thread_count"],
1340 &[
1341 self.int_type.into(),
1342 self.int_type.into(),
1343 self.int_type.into(),
1344 ],
1345 None,
1346 false,
1347 );
1348
1349 let _entry_block = self.start_function(function, None);
1350 let wait_loop_block = self.context.append_basic_block(function, "wait_loop");
1351 let barrier_done_block = self.context.append_basic_block(function, "barrier_done");
1352
1353 let thread_counter = self.globals.thread_counter.unwrap().as_pointer_value();
1354 let barrier_num = function.get_nth_param(0).unwrap().into_int_value();
1355 let total_barriers = function.get_nth_param(1).unwrap().into_int_value();
1356 let thread_count = function.get_nth_param(2).unwrap().into_int_value();
1357
1358 let twice_total_barriers = self
1359 .builder
1360 .build_int_mul(
1361 total_barriers,
1362 self.int_type.const_int(2, false),
1363 "twice_total_barriers",
1364 )
1365 .unwrap();
1366 let twice_total_barriers_minus_barrier_num = self
1367 .builder
1368 .build_int_sub(
1369 twice_total_barriers,
1370 barrier_num,
1371 "twice_total_barriers_minus_barrier_num",
1372 )
1373 .unwrap();
1374 let twice_total_barriers_minus_barrier_num_times_thread_count = self
1375 .builder
1376 .build_int_mul(
1377 twice_total_barriers_minus_barrier_num,
1378 thread_count,
1379 "twice_total_barriers_minus_barrier_num_times_thread_count",
1380 )
1381 .unwrap();
1382
1383 let i32_type = self.context.i32_type();
1385 let one = i32_type.const_int(1, false);
1386 self.builder.build_atomicrmw(
1387 AtomicRMWBinOp::Add,
1388 thread_counter,
1389 one,
1390 AtomicOrdering::Monotonic,
1391 )?;
1392
1393 self.builder.build_unconditional_branch(wait_loop_block)?;
1395 self.builder.position_at_end(wait_loop_block);
1396
1397 let current_value = self
1398 .builder
1399 .build_load(i32_type, thread_counter, "current_value")?
1400 .into_int_value();
1401 current_value
1402 .as_instruction_value()
1403 .unwrap()
1404 .set_atomic_ordering(AtomicOrdering::Monotonic)
1405 .map_err(|e| anyhow!("Error setting atomic ordering: {:?}", e))?;
1406
1407 let all_threads_done = self.builder.build_int_compare(
1408 IntPredicate::UGE,
1409 current_value,
1410 twice_total_barriers_minus_barrier_num_times_thread_count,
1411 "all_threads_done",
1412 )?;
1413
1414 self.builder.build_conditional_branch(
1415 all_threads_done,
1416 barrier_done_block,
1417 wait_loop_block,
1418 )?;
1419 self.builder.position_at_end(barrier_done_block);
1420
1421 self.builder.build_return(None)?;
1422
1423 if function.verify(true) {
1424 Ok(function)
1425 } else {
1426 function.print_to_stderr();
1427 unsafe {
1428 function.delete();
1429 }
1430 Err(anyhow!("Invalid generated function."))
1431 }
1432 }
1433
1434 fn jit_compile_call_barrier(
1435 &mut self,
1436 barrier_num: IntValue<'ctx>,
1437 total_barriers: IntValue<'ctx>,
1438 ) {
1439 if !self.threaded {
1440 return;
1441 }
1442 let thread_dim = self.get_param("thread_dim");
1443 let thread_dim = self
1444 .builder
1445 .build_load(self.int_type, *thread_dim, "thread_dim")
1446 .unwrap()
1447 .into_int_value();
1448 let barrier = self.get_function("barrier").unwrap();
1449 self.builder
1450 .build_call(
1451 barrier,
1452 &[
1453 BasicMetadataValueEnum::IntValue(barrier_num),
1454 BasicMetadataValueEnum::IntValue(total_barriers),
1455 BasicMetadataValueEnum::IntValue(thread_dim),
1456 ],
1457 "barrier",
1458 )
1459 .unwrap();
1460 }
1461
1462 fn jit_threading_limits(
1463 &mut self,
1464 size: IntValue<'ctx>,
1465 ) -> Result<(
1466 IntValue<'ctx>,
1467 IntValue<'ctx>,
1468 BasicBlock<'ctx>,
1469 BasicBlock<'ctx>,
1470 )> {
1471 let one = self.int_type.const_int(1, false);
1472 let thread_id = self.get_param("thread_id");
1473 let thread_id = self
1474 .builder
1475 .build_load(self.int_type, *thread_id, "thread_id")
1476 .unwrap()
1477 .into_int_value();
1478 let thread_dim = self.get_param("thread_dim");
1479 let thread_dim = self
1480 .builder
1481 .build_load(self.int_type, *thread_dim, "thread_dim")
1482 .unwrap()
1483 .into_int_value();
1484
1485 let i_times_size = self
1487 .builder
1488 .build_int_mul(thread_id, size, "i_times_size")?;
1489 let start = self
1490 .builder
1491 .build_int_unsigned_div(i_times_size, thread_dim, "start")?;
1492
1493 let i_plus_one = self.builder.build_int_add(thread_id, one, "i_plus_one")?;
1495 let i_plus_one_times_size =
1496 self.builder
1497 .build_int_mul(i_plus_one, size, "i_plus_one_times_size")?;
1498 let end = self
1499 .builder
1500 .build_int_unsigned_div(i_plus_one_times_size, thread_dim, "end")?;
1501
1502 let test_done = self.builder.get_insert_block().unwrap();
1503 let next_block = self
1504 .context
1505 .append_basic_block(self.fn_value_opt.unwrap(), "threading_block");
1506 self.builder.position_at_end(next_block);
1507
1508 Ok((start, end, test_done, next_block))
1509 }
1510
1511 fn jit_end_threading(
1512 &mut self,
1513 start: IntValue<'ctx>,
1514 end: IntValue<'ctx>,
1515 test_done: BasicBlock<'ctx>,
1516 next: BasicBlock<'ctx>,
1517 ) -> Result<()> {
1518 let exit = self
1519 .context
1520 .append_basic_block(self.fn_value_opt.unwrap(), "exit");
1521 self.builder.build_unconditional_branch(exit)?;
1522 self.builder.position_at_end(test_done);
1523 let done = self
1525 .builder
1526 .build_int_compare(IntPredicate::EQ, start, end, "done")?;
1527 self.builder.build_conditional_branch(done, exit, next)?;
1528 self.builder.position_at_end(exit);
1529 Ok(())
1530 }
1531
1532 pub fn write_bitcode_to_path(&self, path: &std::path::Path) {
1533 self.module.write_bitcode_to_path(path);
1534 }
1535
1536 fn insert_constants(&mut self, model: &DiscreteModel) {
1537 if let Some(constants) = self.globals.constants.as_ref() {
1538 self.insert_param("constants", constants.as_pointer_value());
1539 for tensor in model.constant_defns() {
1540 self.insert_tensor(tensor, true);
1541 }
1542 }
1543 }
1544
1545 fn insert_data(&mut self, model: &DiscreteModel) {
1546 self.insert_model_index();
1547 self.insert_constants(model);
1548
1549 if let Some(input) = model.input() {
1550 self.insert_tensor(input, false);
1551 }
1552 for tensor in model.input_dep_defns() {
1553 self.insert_tensor(tensor, false);
1554 }
1555 for tensor in model.time_dep_defns() {
1556 self.insert_tensor(tensor, false);
1557 }
1558 for tensor in model.state_dep_defns() {
1559 self.insert_tensor(tensor, false);
1560 }
1561 for tensor in model.state_dep_post_f_defns() {
1562 self.insert_tensor(tensor, false);
1563 }
1564 }
1565
1566 fn pointer_type(context: &'ctx Context, _ty: BasicTypeEnum<'ctx>) -> PointerType<'ctx> {
1567 context.ptr_type(AddressSpace::default())
1568 }
1569
1570 fn fn_pointer_type(context: &'ctx Context, _ty: FunctionType<'ctx>) -> PointerType<'ctx> {
1571 context.ptr_type(AddressSpace::default())
1572 }
1573
1574 fn insert_indices(&mut self) {
1575 if let Some(indices) = self.globals.indices.as_ref() {
1576 let i32_type = self.context.i32_type();
1577 let zero = i32_type.const_int(0, false);
1578 let ptr = unsafe {
1579 indices
1580 .as_pointer_value()
1581 .const_in_bounds_gep(i32_type, &[zero])
1582 };
1583 self.variables.insert("indices".to_owned(), ptr);
1584 }
1585 }
1586
1587 fn insert_model_index(&mut self) {
1588 self.insert_param("model_index", self.globals.model_index.as_pointer_value());
1589 }
1590
1591 fn insert_param(&mut self, name: &str, value: PointerValue<'ctx>) {
1592 self.variables.insert(name.to_owned(), value);
1593 }
1594
1595 fn build_gep<T: BasicType<'ctx>>(
1596 &self,
1597 ty: T,
1598 ptr: PointerValue<'ctx>,
1599 ordered_indexes: &[IntValue<'ctx>],
1600 name: &str,
1601 ) -> Result<PointerValue<'ctx>> {
1602 unsafe {
1603 self.builder
1604 .build_gep(ty, ptr, ordered_indexes, name)
1605 .map_err(|e| e.into())
1606 }
1607 }
1608
1609 fn build_load<T: BasicType<'ctx>>(
1610 &self,
1611 ty: T,
1612 ptr: PointerValue<'ctx>,
1613 name: &str,
1614 ) -> Result<BasicValueEnum<'ctx>> {
1615 self.builder.build_load(ty, ptr, name).map_err(|e| e.into())
1616 }
1617
1618 fn get_ptr_to_index<T: BasicType<'ctx>>(
1619 builder: &Builder<'ctx>,
1620 ty: T,
1621 ptr: &PointerValue<'ctx>,
1622 index: IntValue<'ctx>,
1623 name: &str,
1624 ) -> PointerValue<'ctx> {
1625 unsafe {
1626 builder
1627 .build_in_bounds_gep(ty, *ptr, &[index], name)
1628 .unwrap()
1629 }
1630 }
1631
1632 fn insert_state(&mut self, u: &Tensor) {
1633 let mut data_index = 0;
1634 for blk in u.elmts() {
1635 if let Some(name) = blk.name() {
1636 let ptr = self.variables.get("u").unwrap();
1637 let i = self
1638 .context
1639 .i32_type()
1640 .const_int(data_index.try_into().unwrap(), false);
1641 let alloca = Self::get_ptr_to_index(
1642 &self.create_entry_block_builder(),
1643 self.real_type,
1644 ptr,
1645 i,
1646 blk.name().unwrap(),
1647 );
1648 self.variables.insert(name.to_owned(), alloca);
1649 }
1650 data_index += blk.nnz();
1651 }
1652 }
1653 fn insert_dot_state(&mut self, dudt: &Tensor) {
1654 let mut data_index = 0;
1655 for blk in dudt.elmts() {
1656 if let Some(name) = blk.name() {
1657 let ptr = self.variables.get("dudt").unwrap();
1658 let i = self
1659 .context
1660 .i32_type()
1661 .const_int(data_index.try_into().unwrap(), false);
1662 let alloca = Self::get_ptr_to_index(
1663 &self.create_entry_block_builder(),
1664 self.real_type,
1665 ptr,
1666 i,
1667 blk.name().unwrap(),
1668 );
1669 self.variables.insert(name.to_owned(), alloca);
1670 }
1671 data_index += blk.nnz();
1672 }
1673 }
1674 fn insert_tensor(&mut self, tensor: &Tensor, is_constant: bool) {
1675 let var_name = if is_constant { "constants" } else { "data" };
1676 let ptr = *self.variables.get(var_name).unwrap();
1677 let mut data_index = self.layout.get_data_index(tensor.name()).unwrap();
1678 let i = self
1679 .context
1680 .i32_type()
1681 .const_int(data_index.try_into().unwrap(), false);
1682 let alloca = Self::get_ptr_to_index(
1683 &self.create_entry_block_builder(),
1684 self.real_type,
1685 &ptr,
1686 i,
1687 tensor.name(),
1688 );
1689 self.variables.insert(tensor.name().to_owned(), alloca);
1690
1691 for blk in tensor.elmts() {
1693 if let Some(name) = blk.name() {
1694 let i = self
1695 .context
1696 .i32_type()
1697 .const_int(data_index.try_into().unwrap(), false);
1698 let alloca = Self::get_ptr_to_index(
1699 &self.create_entry_block_builder(),
1700 self.real_type,
1701 &ptr,
1702 i,
1703 name,
1704 );
1705 self.variables.insert(name.to_owned(), alloca);
1706 }
1707 data_index += blk.nnz();
1709 }
1710 }
1711 fn get_param(&self, name: &str) -> &PointerValue<'ctx> {
1712 self.variables.get(name).unwrap()
1713 }
1714
1715 fn get_var(&self, tensor: &Tensor) -> &PointerValue<'ctx> {
1716 self.variables.get(tensor.name()).unwrap()
1717 }
1718
1719 fn get_function(&mut self, name: &str) -> Option<FunctionValue<'ctx>> {
1720 if let Some(&func) = self.functions.get(name) {
1722 return Some(func);
1723 }
1724 let current_block = self.builder.get_insert_block().unwrap();
1725 let current_loc = self.builder.get_current_debug_location().unwrap();
1726 let current_function = self.fn_value_opt.unwrap();
1727
1728 let function = match name {
1729 "sin" | "cos" | "tan" | "exp" | "log" | "log10" | "sqrt" | "abs" | "copysign"
1731 | "pow" | "min" | "max" => {
1732 let intrinsic_name = match name {
1733 "min" => "minnum",
1734 "max" => "maxnum",
1735 "abs" => "fabs",
1736 _ => name,
1737 };
1738 let llvm_name =
1739 format!("llvm.{}.{}", intrinsic_name, self.diffsl_real_type.as_str());
1740
1741 let args_types: Vec<BasicTypeEnum> = vec![self.real_type.into()];
1742 self.builder.unset_current_debug_location();
1743
1744 Some(match Intrinsic::find(&llvm_name) {
1746 Some(intrinsic) => intrinsic.get_declaration(&self.module, &args_types)?,
1747 None => {
1748 let args_types_meta: Vec<BasicMetadataTypeEnum> =
1750 vec![self.real_type.into()];
1751 let function_type = self.real_type.fn_type(&args_types_meta, false);
1752 self.module
1753 .add_function(name, function_type, Some(Linkage::External))
1754 }
1755 })
1756 }
1757 "sigmoid" => {
1759 let arg_len = 1;
1760 let ret_type = self.real_type;
1761
1762 let args_types = std::iter::repeat_n(ret_type, arg_len)
1763 .map(|f| f.into())
1764 .collect::<Vec<BasicMetadataTypeEnum>>();
1765
1766 let fn_val = self.add_function(name, &["x"], &args_types, None, true);
1767
1768 for arg in fn_val.get_param_iter() {
1769 arg.into_float_value().set_name("x");
1770 }
1771 let _basic_block = self.start_function(fn_val, None);
1772
1773 let x = fn_val.get_nth_param(0)?.into_float_value();
1774 let one = self.real_type.const_float(1.0);
1775 let negx = self.builder.build_float_neg(x, name).ok()?;
1776 let exp = self.get_function("exp").unwrap();
1777 let exp_negx = self
1778 .builder
1779 .build_call(exp, &[BasicMetadataValueEnum::FloatValue(negx)], name)
1780 .ok()?;
1781 let one_plus_exp_negx = self
1782 .builder
1783 .build_float_add(
1784 exp_negx
1785 .try_as_basic_value()
1786 .unwrap_basic()
1787 .into_float_value(),
1788 one,
1789 name,
1790 )
1791 .ok()?;
1792 let sigmoid = self
1793 .builder
1794 .build_float_div(one, one_plus_exp_negx, name)
1795 .ok()?;
1796 self.builder.build_return(Some(&sigmoid)).ok();
1797 Some(fn_val)
1798 }
1799 "arcsinh" | "arccosh" => {
1800 let arg_len = 1;
1801 let ret_type = self.real_type;
1802
1803 let args_types = std::iter::repeat_n(ret_type, arg_len)
1804 .map(|f| f.into())
1805 .collect::<Vec<BasicMetadataTypeEnum>>();
1806
1807 let fn_val = self.add_function(name, &["x"], &args_types, None, true);
1808
1809 for arg in fn_val.get_param_iter() {
1810 arg.into_float_value().set_name("x");
1811 }
1812
1813 let _basic_block = self.start_function(fn_val, None);
1814 let x = fn_val.get_nth_param(0)?.into_float_value();
1815 let one = match name {
1816 "arccosh" => self.real_type.const_float(-1.0),
1817 "arcsinh" => self.real_type.const_float(1.0),
1818 _ => panic!("unknown function"),
1819 };
1820 let x_squared = self.builder.build_float_mul(x, x, name).ok()?;
1821 let one_plus_x_squared = self.builder.build_float_add(x_squared, one, name).ok()?;
1822 let sqrt = self.get_function("sqrt").unwrap();
1823 let sqrt_one_plus_x_squared = self
1824 .builder
1825 .build_call(
1826 sqrt,
1827 &[BasicMetadataValueEnum::FloatValue(one_plus_x_squared)],
1828 name,
1829 )
1830 .unwrap()
1831 .try_as_basic_value()
1832 .unwrap_basic()
1833 .into_float_value();
1834 let x_plus_sqrt_one_plus_x_squared = self
1835 .builder
1836 .build_float_add(x, sqrt_one_plus_x_squared, name)
1837 .ok()?;
1838 let ln = self.get_function("log").unwrap();
1839 let result = self
1840 .builder
1841 .build_call(
1842 ln,
1843 &[BasicMetadataValueEnum::FloatValue(
1844 x_plus_sqrt_one_plus_x_squared,
1845 )],
1846 name,
1847 )
1848 .unwrap()
1849 .try_as_basic_value()
1850 .unwrap_basic()
1851 .into_float_value();
1852 self.builder.build_return(Some(&result)).ok();
1853 Some(fn_val)
1854 }
1855 "heaviside" => {
1856 let arg_len = 1;
1857 let ret_type = self.real_type;
1858
1859 let args_types = std::iter::repeat_n(ret_type, arg_len)
1860 .map(|f| f.into())
1861 .collect::<Vec<BasicMetadataTypeEnum>>();
1862
1863 let fn_val = self.add_function(name, &["x"], &args_types, None, true);
1864
1865 for arg in fn_val.get_param_iter() {
1866 arg.into_float_value().set_name("x");
1867 }
1868
1869 let _basic_block = self.start_function(fn_val, None);
1870 let x = fn_val.get_nth_param(0)?.into_float_value();
1871 let zero = self.real_type.const_float(0.0);
1872 let one = self.real_type.const_float(1.0);
1873 let result = self
1874 .builder
1875 .build_select(
1876 self.builder
1877 .build_float_compare(FloatPredicate::OGE, x, zero, "x >= 0")
1878 .unwrap(),
1879 one,
1880 zero,
1881 name,
1882 )
1883 .ok()?;
1884 self.builder.build_return(Some(&result)).ok();
1885 Some(fn_val)
1886 }
1887 "tanh" | "sinh" | "cosh" => {
1888 let arg_len = 1;
1889 let ret_type = self.real_type;
1890
1891 let args_types = std::iter::repeat_n(ret_type, arg_len)
1892 .map(|f| f.into())
1893 .collect::<Vec<BasicMetadataTypeEnum>>();
1894
1895 let fn_val = self.add_function(name, &["x"], &args_types, None, true);
1896
1897 for arg in fn_val.get_param_iter() {
1898 arg.into_float_value().set_name("x");
1899 }
1900
1901 let _basic_block = self.start_function(fn_val, None);
1902 let x = fn_val.get_nth_param(0)?.into_float_value();
1903 let negx = self.builder.build_float_neg(x, name).ok()?;
1904 let exp = self.get_function("exp").unwrap();
1905 let exp_negx = self
1906 .builder
1907 .build_call(exp, &[BasicMetadataValueEnum::FloatValue(negx)], name)
1908 .ok()?;
1909 let expx = self
1910 .builder
1911 .build_call(exp, &[BasicMetadataValueEnum::FloatValue(x)], name)
1912 .ok()?;
1913 let expx_minus_exp_negx = self
1914 .builder
1915 .build_float_sub(
1916 expx.try_as_basic_value().unwrap_basic().into_float_value(),
1917 exp_negx
1918 .try_as_basic_value()
1919 .unwrap_basic()
1920 .into_float_value(),
1921 name,
1922 )
1923 .ok()?;
1924 let expx_plus_exp_negx = self
1925 .builder
1926 .build_float_add(
1927 expx.try_as_basic_value().unwrap_basic().into_float_value(),
1928 exp_negx
1929 .try_as_basic_value()
1930 .unwrap_basic()
1931 .into_float_value(),
1932 name,
1933 )
1934 .ok()?;
1935 let result = match name {
1936 "tanh" => self
1937 .builder
1938 .build_float_div(expx_minus_exp_negx, expx_plus_exp_negx, name)
1939 .ok()?,
1940 "sinh" => self
1941 .builder
1942 .build_float_div(expx_minus_exp_negx, self.real_type.const_float(2.0), name)
1943 .ok()?,
1944 "cosh" => self
1945 .builder
1946 .build_float_div(expx_plus_exp_negx, self.real_type.const_float(2.0), name)
1947 .ok()?,
1948 _ => panic!("unknown function"),
1949 };
1950 self.builder.build_return(Some(&result)).ok();
1951 Some(fn_val)
1952 }
1953 "interp1d_impl" => self.compile_interp1d_impl(name).ok(),
1954 _ => None,
1955 }?;
1956
1957 if !function.verify(true) {
1958 function.print_to_stderr();
1959 panic!("Invalid generated function for {}", name);
1960 }
1961
1962 self.builder.position_at_end(current_block);
1963 if self.dibuilder.is_some() {
1964 self.builder.set_current_debug_location(current_loc);
1965 }
1966 self.fn_value_opt = Some(current_function);
1967
1968 self.functions.insert(name.to_owned(), function);
1969 Some(function)
1970 }
1971
1972 fn compile_interp1d_impl(&mut self, name: &str) -> Result<FunctionValue<'ctx>> {
1987 let ret_type = self.real_type;
1988 let args_types = vec![
1989 self.real_ptr_type.into(), self.int_type.into(), self.int_type.into(), self.int_type.into(), ret_type.into(), ];
1995 let fn_val = self.add_function(
1996 name,
1997 &["constants", "x_off", "y_off", "n", "q"],
1998 &args_types,
1999 None,
2000 true,
2001 );
2002
2003 let entry = self.start_function(fn_val, None);
2004 let constants_ptr = fn_val
2005 .get_nth_param(0)
2006 .ok_or_else(|| anyhow!("missing param 0"))?
2007 .into_pointer_value();
2008 let x_off = fn_val
2009 .get_nth_param(1)
2010 .ok_or_else(|| anyhow!("missing param 1"))?
2011 .into_int_value();
2012 let y_off = fn_val
2013 .get_nth_param(2)
2014 .ok_or_else(|| anyhow!("missing param 2"))?
2015 .into_int_value();
2016 let n_val = fn_val
2017 .get_nth_param(3)
2018 .ok_or_else(|| anyhow!("missing param 3"))?
2019 .into_int_value();
2020 let q = fn_val
2021 .get_nth_param(4)
2022 .ok_or_else(|| anyhow!("missing param 4"))?
2023 .into_float_value();
2024
2025 let x0_ptr = self.build_gep(self.real_type, constants_ptr, &[x_off], "x0_ptr")?;
2027 let x0 = self
2028 .build_load(self.real_type, x0_ptr, "x0")?
2029 .into_float_value();
2030 let one = self.int_type.const_int(1, false);
2031 let nm1 = self.builder.build_int_sub(n_val, one, "nm1")?;
2032 let xn_off = self.builder.build_int_add(x_off, nm1, "xn_off")?;
2033 let xn_ptr = self.build_gep(self.real_type, constants_ptr, &[xn_off], "xn_ptr")?;
2034 let xn = self
2035 .build_load(self.real_type, xn_ptr, "xn")?
2036 .into_float_value();
2037 let q_lt_xn = self
2038 .builder
2039 .build_float_compare(FloatPredicate::OLT, q, xn, "q_lt_xn")?;
2040 let q = self
2041 .builder
2042 .build_select(q_lt_xn, q, xn, "q_min")?
2043 .into_float_value();
2044 let q_gt_x0 = self
2045 .builder
2046 .build_float_compare(FloatPredicate::OGT, q, x0, "q_gt_x0")?;
2047 let q = self
2048 .builder
2049 .build_select(q_gt_x0, q, x0, "q_max")?
2050 .into_float_value();
2051
2052 let loop_block = self.context.append_basic_block(fn_val, "loop");
2054 let exit_block = self.context.append_basic_block(fn_val, "exit");
2055 self.builder.build_unconditional_branch(loop_block)?;
2056 self.builder.position_at_end(loop_block);
2057
2058 let lo = self.builder.build_phi(self.int_type, "lo")?;
2059 let hi = self.builder.build_phi(self.int_type, "hi")?;
2060 let zero = self.int_type.const_zero();
2061 let nm2 = self
2062 .builder
2063 .build_int_sub(n_val, self.int_type.const_int(2, false), "nm2")?;
2064 lo.add_incoming(&[(&zero, entry)]);
2065 hi.add_incoming(&[(&nm1, entry)]);
2066
2067 let lo_v = lo.as_basic_value().into_int_value();
2068 let hi_v = hi.as_basic_value().into_int_value();
2069 let cmp = self
2070 .builder
2071 .build_int_compare(IntPredicate::SLT, lo_v, hi_v, "cmp")?;
2072 let body_block = self.context.append_basic_block(fn_val, "body");
2073 self.builder
2074 .build_conditional_branch(cmp, body_block, exit_block)?;
2075
2076 self.builder.position_at_end(body_block);
2078 let sum = self.builder.build_int_add(lo_v, hi_v, "sum")?;
2079 let sum1 = self.builder.build_int_add(sum, one, "sum1")?;
2080 let two = self.int_type.const_int(2, false);
2081 let mid = self.builder.build_int_unsigned_div(sum1, two, "mid")?;
2082 let x_mid_off = self.builder.build_int_add(x_off, mid, "x_mid_off")?;
2083 let x_mid_ptr = self.build_gep(self.real_type, constants_ptr, &[x_mid_off], "x_mid_ptr")?;
2084 let x_mid = self
2085 .build_load(self.real_type, x_mid_ptr, "x_mid")?
2086 .into_float_value();
2087 let x_le_q = self
2088 .builder
2089 .build_float_compare(FloatPredicate::OLE, x_mid, q, "x_le_q")?;
2090 let mid_m1 = self.builder.build_int_sub(mid, one, "mid_m1")?;
2091 let new_lo = self
2092 .builder
2093 .build_select(x_le_q, mid, lo_v, "new_lo")?
2094 .into_int_value();
2095 let new_hi = self
2096 .builder
2097 .build_select(x_le_q, hi_v, mid_m1, "new_hi")?
2098 .into_int_value();
2099
2100 self.builder.build_unconditional_branch(loop_block)?;
2102 lo.add_incoming(&[(&new_lo, body_block)]);
2103 hi.add_incoming(&[(&new_hi, body_block)]);
2104
2105 self.builder.position_at_end(exit_block);
2107 let k_raw = lo.as_basic_value().into_int_value();
2108 let k_gt = self
2109 .builder
2110 .build_int_compare(IntPredicate::SGT, k_raw, nm2, "k_gt")?;
2111 let k = self
2112 .builder
2113 .build_select(k_gt, nm2, k_raw, "k")?
2114 .into_int_value();
2115 let kp1 = self.builder.build_int_add(k, one, "kp1")?;
2116
2117 let xk_off = self.builder.build_int_add(x_off, k, "xk_off")?;
2118 let xk1_off = self.builder.build_int_add(x_off, kp1, "xk1_off")?;
2119 let yk_off = self.builder.build_int_add(y_off, k, "yk_off")?;
2120 let yk1_off = self.builder.build_int_add(y_off, kp1, "yk1_off")?;
2121
2122 let xk_ptr = self.build_gep(self.real_type, constants_ptr, &[xk_off], "xk_ptr")?;
2123 let xk = self
2124 .build_load(self.real_type, xk_ptr, "xk")?
2125 .into_float_value();
2126 let xk1_ptr = self.build_gep(self.real_type, constants_ptr, &[xk1_off], "xk1_ptr")?;
2127 let xk1 = self
2128 .build_load(self.real_type, xk1_ptr, "xk1")?
2129 .into_float_value();
2130 let yk_ptr = self.build_gep(self.real_type, constants_ptr, &[yk_off], "yk_ptr")?;
2131 let yk = self
2132 .build_load(self.real_type, yk_ptr, "yk")?
2133 .into_float_value();
2134 let yk1_ptr = self.build_gep(self.real_type, constants_ptr, &[yk1_off], "yk1_ptr")?;
2135 let yk1 = self
2136 .build_load(self.real_type, yk1_ptr, "yk1")?
2137 .into_float_value();
2138
2139 let dx = self.builder.build_float_sub(xk1, xk, "dx")?;
2140 let dq = self.builder.build_float_sub(q, xk, "dq")?;
2141 let t = self.builder.build_float_div(dq, dx, "t")?;
2142 let dy = self.builder.build_float_sub(yk1, yk, "dy")?;
2143 let tdy = self.builder.build_float_mul(t, dy, "tdy")?;
2144 let result = self.builder.build_float_add(yk, tdy, name)?;
2145
2146 self.builder.build_return(Some(&result)).ok();
2147 Ok(fn_val)
2148 }
2149
2150 #[inline]
2152 fn fn_value(&self) -> FunctionValue<'ctx> {
2153 self.fn_value_opt.unwrap()
2154 }
2155
2156 #[inline]
2157 fn tensor_ptr(&self) -> PointerValue<'ctx> {
2158 self.tensor_ptr_opt.unwrap()
2159 }
2160
2161 fn create_entry_block_builder(&self) -> Builder<'ctx> {
2163 let builder = self.context.create_builder();
2164 let entry = self.fn_value().get_first_basic_block().unwrap();
2165 match entry.get_first_instruction() {
2166 Some(first_instr) => builder.position_before(&first_instr),
2167 None => builder.position_at_end(entry),
2168 }
2169 builder
2170 }
2171
2172 fn jit_compile_scalar(
2173 &mut self,
2174 a: &Tensor,
2175 res_ptr_opt: Option<PointerValue<'ctx>>,
2176 ) -> Result<PointerValue<'ctx>> {
2177 let res_type = self.real_type;
2178 let res_ptr = match res_ptr_opt {
2179 Some(ptr) => ptr,
2180 None => self
2181 .create_entry_block_builder()
2182 .build_alloca(res_type, a.name())?,
2183 };
2184 let name = a.name();
2185 let elmt = a.elmts().first().unwrap();
2186
2187 let curr_block = self.builder.get_insert_block().unwrap();
2189 let mut next_block_opt = None;
2190 if self.threaded {
2191 let next_block = self.context.append_basic_block(self.fn_value(), "next");
2192 self.builder.position_at_end(next_block);
2193 next_block_opt = Some(next_block);
2194 }
2195
2196 let zero = self.int_type.const_zero();
2197 let float_value = self.jit_compile_expr(name, elmt.expr(), &[], elmt, zero)?;
2198 self.builder.build_store(res_ptr, float_value)?;
2199
2200 if self.threaded {
2202 let exit_block = self.context.append_basic_block(self.fn_value(), "exit");
2203 self.builder.build_unconditional_branch(exit_block)?;
2204 self.builder.position_at_end(curr_block);
2205
2206 let thread_id = self.get_param("thread_id");
2207 let thread_id = self
2208 .builder
2209 .build_load(self.int_type, *thread_id, "thread_id")
2210 .unwrap()
2211 .into_int_value();
2212 let is_first_thread = self.builder.build_int_compare(
2213 IntPredicate::EQ,
2214 thread_id,
2215 self.int_type.const_zero(),
2216 "is_first_thread",
2217 )?;
2218 self.builder.build_conditional_branch(
2219 is_first_thread,
2220 next_block_opt.unwrap(),
2221 exit_block,
2222 )?;
2223
2224 self.builder.position_at_end(exit_block);
2225 }
2226
2227 Ok(res_ptr)
2228 }
2229
2230 fn jit_compile_tensor(
2231 &mut self,
2232 a: &Tensor,
2233 res_ptr_opt: Option<PointerValue<'ctx>>,
2234 code: Option<&str>,
2235 ) -> Result<PointerValue<'ctx>> {
2236 if a.rank() == 0
2238 && a.elmts()
2239 .first()
2240 .is_none_or(|b| b.expr_layout().rank() == 0)
2241 {
2242 return self.jit_compile_scalar(a, res_ptr_opt);
2243 }
2244
2245 let res_type = self.real_type;
2246 let res_ptr = match res_ptr_opt {
2247 Some(ptr) => ptr,
2248 None => self
2249 .create_entry_block_builder()
2250 .build_alloca(res_type, a.name())?,
2251 };
2252
2253 self.tensor_ptr_opt = Some(res_ptr);
2255
2256 for (i, blk) in a.elmts().iter().enumerate() {
2257 if blk.has_values() {
2258 continue;
2259 }
2260 let default = format!("{}-{}", a.name(), i);
2261 let name = blk.name().unwrap_or(default.as_str());
2262 self.jit_compile_block(name, a, blk, code)?;
2263 }
2264 Ok(res_ptr)
2265 }
2266
2267 fn jit_compile_block(
2268 &mut self,
2269 name: &str,
2270 tensor: &Tensor,
2271 elmt: &TensorBlock,
2272 code: Option<&str>,
2273 ) -> Result<()> {
2274 let translation = Translation::new(
2275 elmt.expr_layout(),
2276 elmt.layout(),
2277 elmt.start(),
2278 tensor.layout_ptr(),
2279 );
2280
2281 if let Some(dibuilder) = &self.dibuilder {
2282 let (line_no, column_no) = match (elmt.expr().span, code) {
2283 (Some(s), Some(c)) => {
2284 let s = Span::new(c, s.pos_start, s.pos_end).unwrap();
2285 s.start_pos().line_col()
2286 }
2287 _ => (0, 0),
2288 };
2289 let scope = self
2290 .fn_value_opt
2291 .unwrap()
2292 .get_subprogram()
2293 .unwrap()
2294 .as_debug_info_scope();
2295 let loc = dibuilder.create_debug_location(
2296 self.context,
2297 line_no.try_into().unwrap(),
2298 column_no.try_into().unwrap(),
2299 scope,
2300 None,
2301 );
2302 self.builder.set_current_debug_location(loc);
2303 }
2304
2305 if elmt.expr_layout().is_dense()
2308 || matches!(translation.source, TranslationFrom::DenseContraction { .. })
2309 {
2310 self.jit_compile_dense_block(name, elmt, &translation)
2311 } else if elmt.expr_layout().is_diagonal() {
2312 self.jit_compile_diagonal_block(name, elmt, &translation)
2313 } else if elmt.expr_layout().is_sparse() {
2314 match translation.source {
2315 TranslationFrom::SparseContraction { .. } => {
2316 self.jit_compile_sparse_contraction_block(name, elmt, &translation)
2317 }
2318 _ => self.jit_compile_sparse_block(name, elmt, &translation),
2319 }
2320 } else {
2321 Err(anyhow!(
2322 "unsupported block layout: {:?}",
2323 elmt.expr_layout()
2324 ))
2325 }
2326 }
2327
2328 fn jit_compile_dense_block(
2330 &mut self,
2331 name: &str,
2332 elmt: &TensorBlock,
2333 translation: &Translation,
2334 ) -> Result<()> {
2335 let int_type = self.int_type;
2336
2337 let mut preblock = self.builder.get_insert_block().unwrap();
2338 let expr_rank = elmt.expr_layout().rank();
2339 let expr_shape = elmt
2340 .expr_layout()
2341 .shape()
2342 .mapv(|n| int_type.const_int(n.try_into().unwrap(), false));
2343 let one = int_type.const_int(1, false);
2344
2345 let mut expr_strides = vec![1; expr_rank];
2346 if expr_rank > 0 {
2347 for i in (0..expr_rank - 1).rev() {
2348 expr_strides[i] = expr_strides[i + 1] * elmt.expr_layout().shape()[i + 1];
2349 }
2350 }
2351 let expr_strides = expr_strides
2352 .iter()
2353 .map(|&s| int_type.const_int(s.try_into().unwrap(), false))
2354 .collect::<Vec<IntValue>>();
2355
2356 let mut indices = Vec::new();
2358 let mut blocks = Vec::new();
2359
2360 let (contract_sum, contract_by, contract_strides) =
2362 if let TranslationFrom::DenseContraction {
2363 contract_by,
2364 contract_len: _,
2365 } = translation.source
2366 {
2367 let contract_rank = expr_rank - contract_by;
2368 let mut contract_strides = vec![1; contract_rank];
2369 for i in (0..contract_rank.saturating_sub(1)).rev() {
2370 contract_strides[i] =
2371 contract_strides[i + 1] * elmt.expr_layout().shape()[i + 1];
2372 }
2373 let contract_strides = contract_strides
2374 .iter()
2375 .map(|&s| int_type.const_int(s.try_into().unwrap(), false))
2376 .collect::<Vec<IntValue>>();
2377 (
2378 Some(self.builder.build_alloca(self.real_type, "contract_sum")?),
2379 contract_by,
2380 Some(contract_strides),
2381 )
2382 } else {
2383 (None, 0, None)
2384 };
2385
2386 let (thread_start, thread_end, test_done, next) = if self.threaded {
2388 let (start, end, test_done, next) =
2389 self.jit_threading_limits(*expr_shape.get(0).unwrap_or(&one))?;
2390 preblock = next;
2391 (Some(start), Some(end), Some(test_done), Some(next))
2392 } else {
2393 (None, None, None, None)
2394 };
2395
2396 if contract_by == expr_rank {
2398 if let Some(contract_sum) = contract_sum {
2399 self.builder
2400 .build_store(contract_sum, self.real_type.const_zero())?;
2401 }
2402 }
2403
2404 for i in 0..expr_rank {
2405 let block = self.context.append_basic_block(self.fn_value(), name);
2406 self.builder.build_unconditional_branch(block)?;
2407 self.builder.position_at_end(block);
2408
2409 let start_index = if i == 0 && self.threaded {
2410 thread_start.unwrap()
2411 } else {
2412 self.int_type.const_zero()
2413 };
2414
2415 let curr_index = self.builder.build_phi(int_type, format!["i{i}"].as_str())?;
2416 curr_index.add_incoming(&[(&start_index, preblock)]);
2417
2418 let init_point = (expr_rank - contract_by).saturating_sub(1);
2419 if i == init_point && contract_by != expr_rank {
2420 if let Some(contract_sum) = contract_sum {
2421 self.builder
2422 .build_store(contract_sum, self.real_type.const_zero())?;
2423 }
2424 }
2425
2426 indices.push(curr_index);
2427 blocks.push(block);
2428 preblock = block;
2429 }
2430
2431 let indices_int: Vec<IntValue> = indices
2432 .iter()
2433 .map(|i| i.as_basic_value().into_int_value())
2434 .collect();
2435
2436 let mut expr_index = *indices_int.last().unwrap_or(&int_type.const_zero());
2438 let mut stride = 1u64;
2439 if !indices.is_empty() {
2440 for i in (0..indices.len() - 1).rev() {
2441 let iname_i = indices_int[i];
2442 let shapei: u64 = elmt.expr_layout().shape()[i + 1].try_into().unwrap();
2443 stride *= shapei;
2444 let stride_intval = self.context.i32_type().const_int(stride, false);
2445 let stride_mul_i = self.builder.build_int_mul(stride_intval, iname_i, name)?;
2446 expr_index = self.builder.build_int_add(expr_index, stride_mul_i, name)?;
2447 }
2448 }
2449
2450 let float_value =
2451 self.jit_compile_expr(name, elmt.expr(), indices_int.as_slice(), elmt, expr_index)?;
2452
2453 if let Some(contract_sum) = contract_sum {
2454 let contract_sum_value = self
2455 .build_load(self.real_type, contract_sum, "contract_sum")?
2456 .into_float_value();
2457 let new_contract_sum_value = self.builder.build_float_add(
2458 contract_sum_value,
2459 float_value,
2460 "new_contract_sum",
2461 )?;
2462 self.builder
2463 .build_store(contract_sum, new_contract_sum_value)?;
2464 } else {
2465 let expr_index = indices_int.iter().zip(expr_strides.iter()).fold(
2466 self.int_type.const_zero(),
2467 |acc, (i, s)| {
2468 let tmp = self.builder.build_int_mul(*i, *s, "expr_index").unwrap();
2469 self.builder.build_int_add(acc, tmp, "acc").unwrap()
2470 },
2471 );
2472 self.jit_compile_broadcast_and_store(name, elmt, float_value, expr_index, translation)?;
2473 }
2474
2475 let mut postblock = self.builder.get_insert_block().unwrap();
2476
2477 for i in (0..expr_rank).rev() {
2479 let next_index = self.builder.build_int_add(indices_int[i], one, name)?;
2481 indices[i].add_incoming(&[(&next_index, postblock)]);
2482 let store_point = (expr_rank - contract_by).saturating_sub(1);
2483 if i == store_point {
2484 if let Some(contract_sum) = contract_sum {
2485 let contract_sum_value = self
2486 .build_load(self.real_type, contract_sum, "contract_sum")?
2487 .into_float_value();
2488 let contract_strides = contract_strides.as_ref().unwrap();
2489 let elmt_index = indices_int
2490 .iter()
2491 .take(contract_strides.len())
2492 .zip(contract_strides.iter())
2493 .fold(self.int_type.const_zero(), |acc, (i, s)| {
2494 let tmp = self.builder.build_int_mul(*i, *s, "elmt_index").unwrap();
2495 self.builder.build_int_add(acc, tmp, "acc").unwrap()
2496 });
2497 self.jit_compile_store(
2498 name,
2499 elmt,
2500 elmt_index,
2501 contract_sum_value,
2502 translation,
2503 )?;
2504 }
2505 }
2506
2507 let end_index = if i == 0 && self.threaded {
2508 thread_end.unwrap()
2509 } else {
2510 expr_shape[i]
2511 };
2512
2513 let loop_while =
2515 self.builder
2516 .build_int_compare(IntPredicate::ULT, next_index, end_index, name)?;
2517 let block = self.context.append_basic_block(self.fn_value(), name);
2518 self.builder
2519 .build_conditional_branch(loop_while, blocks[i], block)?;
2520 self.builder.position_at_end(block);
2521 postblock = block;
2522 }
2523
2524 if self.threaded {
2525 self.jit_end_threading(
2526 thread_start.unwrap(),
2527 thread_end.unwrap(),
2528 test_done.unwrap(),
2529 next.unwrap(),
2530 )?;
2531 }
2532 Ok(())
2533 }
2534
2535 fn jit_compile_sparse_contraction_block(
2536 &mut self,
2537 name: &str,
2538 elmt: &TensorBlock,
2539 translation: &Translation,
2540 ) -> Result<()> {
2541 match translation.source {
2542 TranslationFrom::SparseContraction { .. } => {}
2543 _ => {
2544 panic!("expected sparse contraction")
2545 }
2546 }
2547 let int_type = self.int_type;
2548
2549 let translation_index = self
2550 .layout
2551 .get_translation_index(elmt.expr_layout(), elmt.layout())
2552 .unwrap();
2553 let translation_index = translation_index + translation.get_from_index_in_data_layout();
2554
2555 let final_contract_index =
2556 int_type.const_int(elmt.layout().nnz().try_into().unwrap(), false);
2557 let (thread_start, thread_end, test_done, next) = if self.threaded {
2558 let (start, end, test_done, next) = self.jit_threading_limits(final_contract_index)?;
2559 (Some(start), Some(end), Some(test_done), Some(next))
2560 } else {
2561 (None, None, None, None)
2562 };
2563
2564 let preblock = self.builder.get_insert_block().unwrap();
2565 let contract_sum_ptr = self.builder.build_alloca(self.real_type, "contract_sum")?;
2566
2567 let block = self.context.append_basic_block(self.fn_value(), name);
2569 self.builder.build_unconditional_branch(block)?;
2570 self.builder.position_at_end(block);
2571
2572 let contract_index = self.builder.build_phi(int_type, "i")?;
2573 let contract_start = if self.threaded {
2574 thread_start.unwrap()
2575 } else {
2576 int_type.const_zero()
2577 };
2578 contract_index.add_incoming(&[(&contract_start, preblock)]);
2579
2580 let start_index = self.builder.build_int_add(
2581 int_type.const_int(translation_index.try_into().unwrap(), false),
2582 self.builder.build_int_mul(
2583 int_type.const_int(2, false),
2584 contract_index.as_basic_value().into_int_value(),
2585 name,
2586 )?,
2587 name,
2588 )?;
2589 let end_index =
2590 self.builder
2591 .build_int_add(start_index, int_type.const_int(1, false), name)?;
2592 let start_ptr = self.build_gep(
2593 self.int_type,
2594 *self.get_param("indices"),
2595 &[start_index],
2596 "start_index_ptr",
2597 )?;
2598 let start_contract = self
2599 .build_load(self.int_type, start_ptr, "start")?
2600 .into_int_value();
2601 let end_ptr = self.build_gep(
2602 self.int_type,
2603 *self.get_param("indices"),
2604 &[end_index],
2605 "end_index_ptr",
2606 )?;
2607 let end_contract = self
2608 .build_load(self.int_type, end_ptr, "end")?
2609 .into_int_value();
2610
2611 self.builder
2613 .build_store(contract_sum_ptr, self.real_type.const_float(0.0))?;
2614
2615 let start_contract_block = self
2617 .context
2618 .append_basic_block(self.fn_value(), format!("{name}_contract").as_str());
2619 self.builder
2620 .build_unconditional_branch(start_contract_block)?;
2621 self.builder.position_at_end(start_contract_block);
2622
2623 let expr_index_phi = self.builder.build_phi(int_type, "j")?;
2624 expr_index_phi.add_incoming(&[(&start_contract, block)]);
2625
2626 let expr_index = expr_index_phi.as_basic_value().into_int_value();
2627 let indices_int = self.expr_indices_from_elmt_index(expr_index, elmt, name)?;
2628
2629 let float_value =
2631 self.jit_compile_expr(name, elmt.expr(), indices_int.as_slice(), elmt, expr_index)?;
2632 let contract_sum_value = self
2633 .build_load(self.real_type, contract_sum_ptr, "contract_sum")?
2634 .into_float_value();
2635 let new_contract_sum_value =
2636 self.builder
2637 .build_float_add(contract_sum_value, float_value, "new_contract_sum")?;
2638 self.builder
2639 .build_store(contract_sum_ptr, new_contract_sum_value)?;
2640
2641 let end_contract_block = self.builder.get_insert_block().unwrap();
2642
2643 let next_elmt_index =
2645 self.builder
2646 .build_int_add(expr_index, int_type.const_int(1, false), name)?;
2647 expr_index_phi.add_incoming(&[(&next_elmt_index, end_contract_block)]);
2648
2649 let loop_while = self.builder.build_int_compare(
2651 IntPredicate::ULT,
2652 next_elmt_index,
2653 end_contract,
2654 name,
2655 )?;
2656 let post_contract_block = self.context.append_basic_block(self.fn_value(), name);
2657 self.builder.build_conditional_branch(
2658 loop_while,
2659 start_contract_block,
2660 post_contract_block,
2661 )?;
2662 self.builder.position_at_end(post_contract_block);
2663
2664 self.jit_compile_store(
2666 name,
2667 elmt,
2668 contract_index.as_basic_value().into_int_value(),
2669 new_contract_sum_value,
2670 translation,
2671 )?;
2672
2673 let next_contract_index = self.builder.build_int_add(
2675 contract_index.as_basic_value().into_int_value(),
2676 int_type.const_int(1, false),
2677 name,
2678 )?;
2679 contract_index.add_incoming(&[(&next_contract_index, post_contract_block)]);
2680
2681 let loop_while = self.builder.build_int_compare(
2683 IntPredicate::ULT,
2684 next_contract_index,
2685 thread_end.unwrap_or(final_contract_index),
2686 name,
2687 )?;
2688 let post_block = self.context.append_basic_block(self.fn_value(), name);
2689 self.builder
2690 .build_conditional_branch(loop_while, block, post_block)?;
2691 self.builder.position_at_end(post_block);
2692
2693 if self.threaded {
2694 self.jit_end_threading(
2695 thread_start.unwrap(),
2696 thread_end.unwrap(),
2697 test_done.unwrap(),
2698 next.unwrap(),
2699 )?;
2700 }
2701
2702 Ok(())
2703 }
2704
2705 fn expr_indices_from_elmt_index(
2706 &mut self,
2707 elmt_index: IntValue<'ctx>,
2708 elmt: &TensorBlock,
2709 name: &str,
2710 ) -> Result<Vec<IntValue<'ctx>>, anyhow::Error> {
2711 let layout_index = self.layout.get_layout_index(elmt.expr_layout()).unwrap();
2712 let int_type = self.int_type;
2713 let elmt_index_mult_rank = self.builder.build_int_mul(
2715 elmt_index,
2716 int_type.const_int(elmt.expr_layout().rank().try_into().unwrap(), false),
2717 name,
2718 )?;
2719 (0..elmt.expr_layout().rank())
2720 .map(|i| {
2721 let layout_index_plus_offset =
2722 int_type.const_int((layout_index + i).try_into().unwrap(), false);
2723 let curr_index = self.builder.build_int_add(
2724 elmt_index_mult_rank,
2725 layout_index_plus_offset,
2726 name,
2727 )?;
2728 let ptr = Self::get_ptr_to_index(
2729 &self.builder,
2730 self.int_type,
2731 self.get_param("indices"),
2732 curr_index,
2733 name,
2734 );
2735 Ok(self.build_load(self.int_type, ptr, name)?.into_int_value())
2736 })
2737 .collect::<Result<Vec<_>, anyhow::Error>>()
2738 }
2739
2740 fn jit_compile_sparse_block(
2743 &mut self,
2744 name: &str,
2745 elmt: &TensorBlock,
2746 translation: &Translation,
2747 ) -> Result<()> {
2748 let int_type = self.int_type;
2749
2750 let start_index = int_type.const_int(0, false);
2751 let end_index = int_type.const_int(elmt.expr_layout().nnz().try_into().unwrap(), false);
2752
2753 let (thread_start, thread_end, test_done, next) = if self.threaded {
2754 let (start, end, test_done, next) = self.jit_threading_limits(end_index)?;
2755 (Some(start), Some(end), Some(test_done), Some(next))
2756 } else {
2757 (None, None, None, None)
2758 };
2759
2760 let preblock = self.builder.get_insert_block().unwrap();
2762 let loop_block = self.context.append_basic_block(self.fn_value(), name);
2763 self.builder.build_unconditional_branch(loop_block)?;
2764 self.builder.position_at_end(loop_block);
2765
2766 let curr_index = self.builder.build_phi(int_type, "i")?;
2767 curr_index.add_incoming(&[(&thread_start.unwrap_or(start_index), preblock)]);
2768
2769 let elmt_index = curr_index.as_basic_value().into_int_value();
2770 let indices_int = self.expr_indices_from_elmt_index(elmt_index, elmt, name)?;
2771
2772 let float_value =
2774 self.jit_compile_expr(name, elmt.expr(), indices_int.as_slice(), elmt, elmt_index)?;
2775
2776 self.jit_compile_broadcast_and_store(name, elmt, float_value, elmt_index, translation)?;
2777
2778 let end_loop_block = self.builder.get_insert_block().unwrap();
2780
2781 let one = int_type.const_int(1, false);
2783 let next_index = self.builder.build_int_add(elmt_index, one, name)?;
2784 curr_index.add_incoming(&[(&next_index, end_loop_block)]);
2785
2786 let loop_while = self.builder.build_int_compare(
2788 IntPredicate::ULT,
2789 next_index,
2790 thread_end.unwrap_or(end_index),
2791 name,
2792 )?;
2793 let post_block = self.context.append_basic_block(self.fn_value(), name);
2794 self.builder
2795 .build_conditional_branch(loop_while, loop_block, post_block)?;
2796 self.builder.position_at_end(post_block);
2797
2798 if self.threaded {
2799 self.jit_end_threading(
2800 thread_start.unwrap(),
2801 thread_end.unwrap(),
2802 test_done.unwrap(),
2803 next.unwrap(),
2804 )?;
2805 }
2806
2807 Ok(())
2808 }
2809
2810 fn jit_compile_diagonal_block(
2812 &mut self,
2813 name: &str,
2814 elmt: &TensorBlock,
2815 translation: &Translation,
2816 ) -> Result<()> {
2817 let int_type = self.int_type;
2818
2819 let start_index = int_type.const_int(0, false);
2820 let end_index = int_type.const_int(elmt.expr_layout().nnz().try_into().unwrap(), false);
2821
2822 let (thread_start, thread_end, test_done, next) = if self.threaded {
2823 let (start, end, test_done, next) = self.jit_threading_limits(end_index)?;
2824 (Some(start), Some(end), Some(test_done), Some(next))
2825 } else {
2826 (None, None, None, None)
2827 };
2828
2829 let preblock = self.builder.get_insert_block().unwrap();
2831 let start_loop_block = self.context.append_basic_block(self.fn_value(), name);
2832 self.builder.build_unconditional_branch(start_loop_block)?;
2833 self.builder.position_at_end(start_loop_block);
2834
2835 let curr_index = self.builder.build_phi(int_type, "i")?;
2836 curr_index.add_incoming(&[(&thread_start.unwrap_or(start_index), preblock)]);
2837
2838 let elmt_index = curr_index.as_basic_value().into_int_value();
2840 let indices_int: Vec<IntValue> =
2841 (0..elmt.expr_layout().rank()).map(|_| elmt_index).collect();
2842
2843 let float_value =
2845 self.jit_compile_expr(name, elmt.expr(), indices_int.as_slice(), elmt, elmt_index)?;
2846
2847 self.jit_compile_broadcast_and_store(name, elmt, float_value, elmt_index, translation)?;
2849
2850 let end_loop_block = self.builder.get_insert_block().unwrap();
2851
2852 let one = int_type.const_int(1, false);
2854 let next_index = self.builder.build_int_add(elmt_index, one, name)?;
2855 curr_index.add_incoming(&[(&next_index, end_loop_block)]);
2856
2857 let loop_while = self.builder.build_int_compare(
2859 IntPredicate::ULT,
2860 next_index,
2861 thread_end.unwrap_or(end_index),
2862 name,
2863 )?;
2864 let post_block = self.context.append_basic_block(self.fn_value(), name);
2865 self.builder
2866 .build_conditional_branch(loop_while, start_loop_block, post_block)?;
2867 self.builder.position_at_end(post_block);
2868
2869 if self.threaded {
2870 self.jit_end_threading(
2871 thread_start.unwrap(),
2872 thread_end.unwrap(),
2873 test_done.unwrap(),
2874 next.unwrap(),
2875 )?;
2876 }
2877
2878 Ok(())
2879 }
2880
2881 fn jit_compile_broadcast_and_store(
2882 &mut self,
2883 name: &str,
2884 elmt: &TensorBlock,
2885 float_value: FloatValue<'ctx>,
2886 expr_index: IntValue<'ctx>,
2887 translation: &Translation,
2888 ) -> Result<()> {
2889 let int_type = self.int_type;
2890 let one = int_type.const_int(1, false);
2891 let zero = int_type.const_int(0, false);
2892 let pre_block = self.builder.get_insert_block().unwrap();
2893 match translation.source {
2894 TranslationFrom::Broadcast {
2895 broadcast_by: _,
2896 broadcast_len,
2897 } => {
2898 let bcast_start_index = zero;
2899 let bcast_end_index = int_type.const_int(broadcast_len.try_into().unwrap(), false);
2900
2901 let bcast_block = self.context.append_basic_block(self.fn_value(), name);
2903 self.builder.build_unconditional_branch(bcast_block)?;
2904 self.builder.position_at_end(bcast_block);
2905 let bcast_index = self.builder.build_phi(int_type, "broadcast_index")?;
2906 bcast_index.add_incoming(&[(&bcast_start_index, pre_block)]);
2907
2908 let store_index = self.builder.build_int_add(
2910 self.builder
2911 .build_int_mul(expr_index, bcast_end_index, "store_index")?,
2912 bcast_index.as_basic_value().into_int_value(),
2913 "bcast_store_index",
2914 )?;
2915 self.jit_compile_store(name, elmt, store_index, float_value, translation)?;
2916
2917 let bcast_next_index = self.builder.build_int_add(
2919 bcast_index.as_basic_value().into_int_value(),
2920 one,
2921 name,
2922 )?;
2923 bcast_index.add_incoming(&[(&bcast_next_index, bcast_block)]);
2924
2925 let bcast_cond = self.builder.build_int_compare(
2927 IntPredicate::ULT,
2928 bcast_next_index,
2929 bcast_end_index,
2930 "broadcast_cond",
2931 )?;
2932 let post_bcast_block = self.context.append_basic_block(self.fn_value(), name);
2933 self.builder
2934 .build_conditional_branch(bcast_cond, bcast_block, post_bcast_block)?;
2935 self.builder.position_at_end(post_bcast_block);
2936 Ok(())
2937 }
2938 TranslationFrom::ElementWise | TranslationFrom::DiagonalContraction { .. } => {
2939 self.jit_compile_store(name, elmt, expr_index, float_value, translation)?;
2940 Ok(())
2941 }
2942 _ => Err(anyhow!("Invalid translation")),
2943 }
2944 }
2945
2946 fn jit_compile_store(
2947 &mut self,
2948 name: &str,
2949 elmt: &TensorBlock,
2950 store_index: IntValue<'ctx>,
2951 float_value: FloatValue<'ctx>,
2952 translation: &Translation,
2953 ) -> Result<()> {
2954 let int_type = self.int_type;
2955 let res_index = match &translation.target {
2956 TranslationTo::Contiguous { start, end: _ } => {
2957 let start_const = int_type.const_int((*start).try_into().unwrap(), false);
2958 self.builder.build_int_add(start_const, store_index, name)?
2959 }
2960 TranslationTo::Sparse { indices: _ } => {
2961 let translate_index = self
2963 .layout
2964 .get_translation_index(elmt.expr_layout(), elmt.layout())
2965 .unwrap();
2966 let translate_store_index =
2967 translate_index + translation.get_to_index_in_data_layout();
2968 let translate_store_index =
2969 int_type.const_int(translate_store_index.try_into().unwrap(), false);
2970 let elmt_index_strided = store_index;
2971 let curr_index =
2972 self.builder
2973 .build_int_add(elmt_index_strided, translate_store_index, name)?;
2974 let ptr = Self::get_ptr_to_index(
2975 &self.builder,
2976 self.int_type,
2977 self.get_param("indices"),
2978 curr_index,
2979 name,
2980 );
2981 self.build_load(self.int_type, ptr, name)?.into_int_value()
2982 }
2983 };
2984
2985 let resi_ptr = Self::get_ptr_to_index(
2986 &self.builder,
2987 self.real_type,
2988 &self.tensor_ptr(),
2989 res_index,
2990 name,
2991 );
2992 self.builder.build_store(resi_ptr, float_value)?;
2993 Ok(())
2994 }
2995
2996 fn jit_compile_expr(
2997 &mut self,
2998 name: &str,
2999 expr: &Ast,
3000 index: &[IntValue<'ctx>],
3001 elmt: &TensorBlock,
3002 expr_index: IntValue<'ctx>,
3003 ) -> Result<FloatValue<'ctx>> {
3004 let name = elmt.name().unwrap_or(name);
3005 match &expr.kind {
3006 AstKind::Binop(binop) => {
3007 let lhs =
3008 self.jit_compile_expr(name, binop.left.as_ref(), index, elmt, expr_index)?;
3009 let rhs =
3010 self.jit_compile_expr(name, binop.right.as_ref(), index, elmt, expr_index)?;
3011 match binop.op {
3012 '*' => Ok(self.builder.build_float_mul(lhs, rhs, name)?),
3013 '/' => Ok(self.builder.build_float_div(lhs, rhs, name)?),
3014 '-' => Ok(self.builder.build_float_sub(lhs, rhs, name)?),
3015 '+' => Ok(self.builder.build_float_add(lhs, rhs, name)?),
3016 unknown => Err(anyhow!("unknown binop op '{}'", unknown)),
3017 }
3018 }
3019 AstKind::Monop(monop) => {
3020 let child =
3021 self.jit_compile_expr(name, monop.child.as_ref(), index, elmt, expr_index)?;
3022 match monop.op {
3023 '-' => Ok(self.builder.build_float_neg(child, name)?),
3024 '+' => Ok(child),
3025 unknown => Err(anyhow!("unknown monop op '{}'", unknown)),
3026 }
3027 }
3028 AstKind::Call(call) if call.fn_name == "interp1d" => {
3029 self.jit_compile_interp1d(name, call, index, elmt, expr_index, expr.span)
3030 }
3031 AstKind::Call(call) if call.fn_name == "piecewise" => {
3032 self.jit_compile_piecewise(name, call, index, elmt, expr_index)
3033 }
3034 AstKind::Call(call) => match self.get_function(call.fn_name) {
3035 Some(function) => {
3036 let mut args: Vec<BasicMetadataValueEnum> = Vec::new();
3037 for arg in call.args.iter() {
3038 let arg_val =
3039 self.jit_compile_expr(name, arg.as_ref(), index, elmt, expr_index)?;
3040 args.push(BasicMetadataValueEnum::FloatValue(arg_val));
3041 }
3042 let ret_value = self
3043 .builder
3044 .build_call(function, args.as_slice(), name)?
3045 .try_as_basic_value()
3046 .unwrap_basic()
3047 .into_float_value();
3048 Ok(ret_value)
3049 }
3050 None => Err(anyhow!("unknown function call '{}'", call.fn_name)),
3051 },
3052 AstKind::CallArg(arg) => {
3053 self.jit_compile_expr(name, &arg.expression, index, elmt, expr_index)
3054 }
3055 AstKind::Number(value) => Ok(self.real_type.const_float(*value)),
3056 AstKind::Name(iname) => {
3057 if iname.name == "N" {
3058 if iname.is_tangent {
3059 return Ok(self.real_type.const_float(0.0));
3060 }
3061 let model_index = self
3062 .build_load(self.int_type, *self.get_param("model_index"), "model_index")?
3063 .into_int_value();
3064 let n_value = self.builder.build_signed_int_to_float(
3065 model_index,
3066 self.real_type,
3067 "n_as_real",
3068 )?;
3069 return Ok(n_value);
3070 }
3071 let ptr = *self.get_param(iname.name);
3072 let layout = self.layout.get_layout(iname.name).unwrap().clone();
3073 let iname_elmt_index = if layout.is_dense() {
3074 let mut no_transform = true;
3076 let mut iname_index = Vec::new();
3077 for (i, c) in iname.indices.iter().enumerate() {
3078 let pi = elmt
3081 .indices()
3082 .iter()
3083 .position(|x| x == c)
3084 .unwrap_or(elmt.indices().len());
3085 if let Some(indice_ast) = iname.indice.as_ref() {
3087 let Some(indice) = indice_ast.kind.as_indice() else {
3088 return Err(anyhow!("invalid index expression '{}'", indice_ast));
3089 };
3090 let start_intval =
3091 self.jit_compile_integer_expr(indice.first.as_ref(), name)?;
3092 let index_pi = if indice.last.is_some() {
3093 let index_pi = if pi >= index.len() {
3095 self.context.i32_type().const_int(0, false)
3096 } else {
3097 index[pi]
3098 };
3099 self.builder.build_int_add(index_pi, start_intval, name)?
3100 } else {
3101 start_intval
3103 };
3104 iname_index.push(index_pi);
3105 } else {
3106 let index_pi = if pi >= index.len() {
3107 self.context.i32_type().const_int(0, false)
3108 } else {
3109 index[pi]
3110 };
3111 iname_index.push(index_pi);
3112 }
3113 no_transform = no_transform && pi == i;
3114 }
3115 let iname_index: Vec<IntValue> = iname_index
3117 .iter()
3118 .enumerate()
3119 .map(|(i, &idx)| {
3120 if layout.shape()[i] == 1 {
3121 self.context.i32_type().const_int(0, false)
3122 } else {
3123 idx
3124 }
3125 })
3126 .collect();
3127 if !iname_index.is_empty() {
3130 let mut iname_elmt_index = *iname_index.last().unwrap();
3132 let mut stride = 1u64;
3133 for i in (0..iname_index.len() - 1).rev() {
3134 let iname_i = iname_index[i];
3135 let shapei: u64 = layout.shape()[i + 1].try_into().unwrap();
3136 stride *= shapei;
3137 let stride_intval = self.context.i32_type().const_int(stride, false);
3138 let stride_mul_i =
3139 self.builder.build_int_mul(stride_intval, iname_i, name)?;
3140 iname_elmt_index =
3141 self.builder
3142 .build_int_add(iname_elmt_index, stride_mul_i, name)?;
3143 }
3144 iname_elmt_index
3145 } else {
3146 let zero = self.context.i32_type().const_int(0, false);
3148 zero
3149 }
3150 } else if layout.is_sparse() || layout.is_diagonal() {
3151 let expr_layout = elmt.expr_layout();
3152
3153 if expr_layout != &layout {
3154 let permutation =
3161 DataLayout::permutation(elmt, iname.indices.as_slice(), &layout);
3162 if let Some(base_binary_layout_index) =
3163 self.layout
3164 .get_binary_layout_index(&layout, expr_layout, permutation)
3165 {
3166 let binary_layout_index = self.builder.build_int_add(
3167 self.int_type
3168 .const_int(base_binary_layout_index.try_into().unwrap(), false),
3169 expr_index,
3170 name,
3171 )?;
3172
3173 let indices_ptr = Self::get_ptr_to_index(
3174 &self.builder,
3175 self.int_type,
3176 self.get_param("indices"),
3177 binary_layout_index,
3178 name,
3179 );
3180 let mapped_index = self
3181 .build_load(self.int_type, indices_ptr, name)?
3182 .into_int_value();
3183 let is_less_than_zero = self.builder.build_int_compare(
3184 IntPredicate::SLT,
3185 mapped_index,
3186 self.int_type.const_int(0, true),
3187 "sparse_index_check",
3188 )?;
3189 let is_less_than_zero_block =
3190 self.context.append_basic_block(self.fn_value(), "lt_zero");
3191 let not_less_than_zero_block = self
3192 .context
3193 .append_basic_block(self.fn_value(), "not_lt_zero");
3194 let merge_block =
3195 self.context.append_basic_block(self.fn_value(), "merge");
3196 self.builder.build_conditional_branch(
3197 is_less_than_zero,
3198 is_less_than_zero_block,
3199 not_less_than_zero_block,
3200 )?;
3201
3202 self.builder.position_at_end(is_less_than_zero_block);
3204 let zero_value = self.real_type.const_float(0.);
3205 self.builder.build_unconditional_branch(merge_block)?;
3206
3207 self.builder.position_at_end(not_less_than_zero_block);
3209 let value_ptr = Self::get_ptr_to_index(
3210 &self.builder,
3211 self.real_type,
3212 &ptr,
3213 mapped_index,
3214 name,
3215 );
3216 let value = self
3217 .build_load(self.real_type, value_ptr, name)?
3218 .into_float_value();
3219 self.builder.build_unconditional_branch(merge_block)?;
3220
3221 self.builder.position_at_end(merge_block);
3223 let if_return_value =
3224 self.builder.build_phi(self.real_type, "sparse_value")?;
3225 if_return_value.add_incoming(&[(&zero_value, is_less_than_zero_block)]);
3226 if_return_value.add_incoming(&[(&value, not_less_than_zero_block)]);
3227
3228 let phi_value = if_return_value.as_basic_value().into_float_value();
3229 return Ok(phi_value);
3230 } else {
3231 expr_index
3232 }
3233 } else {
3234 expr_index
3236 }
3237 } else {
3238 panic!("unexpected layout");
3239 };
3240 let value_ptr = Self::get_ptr_to_index(
3241 &self.builder,
3242 self.real_type,
3243 &ptr,
3244 iname_elmt_index,
3245 name,
3246 );
3247 Ok(self
3248 .build_load(self.real_type, value_ptr, name)?
3249 .into_float_value())
3250 }
3251 AstKind::NamedGradient(name) => {
3252 let name_str = name.to_string();
3253 let ptr = self.get_param(name_str.as_str());
3254 Ok(self
3255 .build_load(self.real_type, *ptr, name_str.as_str())?
3256 .into_float_value())
3257 }
3258 AstKind::Index(_) => todo!(),
3259 AstKind::Slice(_) => todo!(),
3260 AstKind::Integer(_) => todo!(),
3261 _ => panic!("unexprected astkind"),
3262 }
3263 }
3264
3265 fn jit_compile_interp1d(
3266 &mut self,
3267 name: &str,
3268 call: &crate::ast::Call,
3269 index: &[IntValue<'ctx>],
3270 elmt: &TensorBlock,
3271 expr_index: IntValue<'ctx>,
3272 span: Option<StringSpan>,
3273 ) -> Result<FloatValue<'ctx>> {
3274 let mut hasher = std::collections::hash_map::DefaultHasher::new();
3275 if call.is_tangent {
3276 std::hash::Hash::hash(&format!("{:?}", call.args[0]), &mut hasher);
3278 std::hash::Hash::hash(&format!("{:?}", call.args[2]), &mut hasher);
3279 std::hash::Hash::hash(&format!("{:?}", call.args[4]), &mut hasher);
3280 } else {
3281 std::hash::Hash::hash(&format!("{:?}", call.args[0]), &mut hasher);
3282 std::hash::Hash::hash(&format!("{:?}", call.args[1]), &mut hasher);
3283 std::hash::Hash::hash(&format!("{:?}", call.args[2]), &mut hasher);
3284 }
3285 let hash_key = std::hash::Hasher::finish(&hasher);
3286 let info = self
3287 .layout
3288 .get_interp1d_info(span.as_ref(), hash_key)
3289 .ok_or_else(|| anyhow!("interp1d: no precomputed data for this call"))?;
3290
3291 let n = info.n;
3292 let x_offset = info.x_offset;
3293 let y_offset = info.y_offset;
3294 let dx_opt = info.dx;
3295
3296 let q = self.jit_compile_expr(name, &call.args[2], index, elmt, expr_index)?;
3297
3298 if let Some(dx) = dx_opt {
3299 let constants_slice = self.layout.constants();
3301 let x_first = self.real_type.const_float(constants_slice[x_offset]);
3302 let x_last = self
3303 .real_type
3304 .const_float(constants_slice[x_offset + n - 1]);
3305 let q_lt_last =
3306 self.builder
3307 .build_float_compare(FloatPredicate::OLT, q, x_last, "q_lt_last")?;
3308 let q = self
3309 .builder
3310 .build_select(q_lt_last, q, x_last, "q_min")?
3311 .into_float_value();
3312 let q_gt_first =
3313 self.builder
3314 .build_float_compare(FloatPredicate::OGT, q, x_first, "q_gt_first")?;
3315 let q = self
3316 .builder
3317 .build_select(q_gt_first, q, x_first, "q_max")?
3318 .into_float_value();
3319
3320 let dx_const = self.real_type.const_float(dx);
3321 let q_minus_x0 = self.builder.build_float_sub(q, x_first, "dq")?;
3322 let k_float = self.builder.build_float_div(q_minus_x0, dx_const, "kf")?;
3323 let k = self
3324 .builder
3325 .build_float_to_signed_int(k_float, self.int_type, "k")?;
3326
3327 let zero = self.int_type.const_zero();
3328 let nm2 = self.int_type.const_int((n - 2) as u64, false);
3329 let k_lt_0 = self
3330 .builder
3331 .build_int_compare(IntPredicate::SLT, k, zero, "k_lt_0")?;
3332 let k = self
3333 .builder
3334 .build_select(k_lt_0, zero, k, "k_clamp_lo")?
3335 .into_int_value();
3336 let k_gt = self
3337 .builder
3338 .build_int_compare(IntPredicate::SGT, k, nm2, "k_gt")?;
3339 let k = self
3340 .builder
3341 .build_select(k_gt, nm2, k, "k_clamp_hi")?
3342 .into_int_value();
3343
3344 let constants_ptr = if let Some(g) = self.globals.constants {
3345 self.builder.build_pointer_cast(
3346 g.as_pointer_value(),
3347 self.real_ptr_type,
3348 "constants_ptr",
3349 )?
3350 } else {
3351 return Err(anyhow!("constants global not available"));
3352 };
3353
3354 let y_off = self.int_type.const_int(y_offset as u64, false);
3355 let one = self.int_type.const_int(1, false);
3356 let yk_off = self.builder.build_int_add(y_off, k, "yk_off")?;
3357 let yk1_off = self.builder.build_int_add(yk_off, one, "yk1_off")?;
3358
3359 let yk_ptr = self.build_gep(self.real_type, constants_ptr, &[yk_off], "yk_ptr")?;
3360 let yk = self
3361 .build_load(self.real_type, yk_ptr, "yk")?
3362 .into_float_value();
3363 let yk1_ptr = self.build_gep(self.real_type, constants_ptr, &[yk1_off], "yk1_ptr")?;
3364 let yk1 = self
3365 .build_load(self.real_type, yk1_ptr, "yk1")?
3366 .into_float_value();
3367
3368 let k_float2 = self
3369 .builder
3370 .build_signed_int_to_float(k, self.real_type, "kf2")?;
3371 let t = self.builder.build_float_sub(k_float, k_float2, "t")?;
3372 let dy = self.builder.build_float_sub(yk1, yk, "dy")?;
3373 let tdy = self.builder.build_float_mul(t, dy, "tdy")?;
3374 return Ok(self.builder.build_float_add(yk, tdy, "r")?);
3375 }
3376
3377 let function = self
3379 .get_function("interp1d_impl")
3380 .ok_or_else(|| anyhow!("interp1d_impl not compiled"))?;
3381
3382 let constants_ptr = if let Some(g) = self.globals.constants {
3383 self.builder.build_pointer_cast(
3384 g.as_pointer_value(),
3385 self.real_ptr_type,
3386 "constants_ptr",
3387 )?
3388 } else {
3389 return Err(anyhow!("constants global not available"));
3390 };
3391
3392 let args: Vec<BasicMetadataValueEnum> = vec![
3393 constants_ptr.into(),
3394 self.int_type.const_int(x_offset as u64, false).into(),
3395 self.int_type.const_int(y_offset as u64, false).into(),
3396 self.int_type.const_int(n as u64, false).into(),
3397 q.into(),
3398 ];
3399 let ret = self
3400 .builder
3401 .build_call(function, &args, name)?
3402 .try_as_basic_value()
3403 .unwrap_basic()
3404 .into_float_value();
3405 Ok(ret)
3406 }
3407
3408 fn jit_compile_piecewise(
3409 &mut self,
3410 name: &str,
3411 call: &crate::ast::Call,
3412 index: &[IntValue<'ctx>],
3413 elmt: &TensorBlock,
3414 expr_index: IntValue<'ctx>,
3415 ) -> Result<FloatValue<'ctx>> {
3416 let n = call.args.len();
3417 let num_branches = (n - 1) / 2;
3418
3419 let fallback = self.jit_compile_expr(name, &call.args[n - 1], index, elmt, expr_index)?;
3420
3421 let mut result = fallback;
3422 for i in (0..num_branches).rev() {
3423 let check = self.jit_compile_expr(name, &call.args[2 * i], index, elmt, expr_index)?;
3424 let value =
3425 self.jit_compile_expr(name, &call.args[2 * i + 1], index, elmt, expr_index)?;
3426 let zero = self.real_type.const_float(0.0);
3427 let cond = self
3428 .builder
3429 .build_float_compare(FloatPredicate::OGE, check, zero, name)?;
3430 result = self
3431 .builder
3432 .build_select(cond, value, result, name)?
3433 .into_float_value();
3434 }
3435 Ok(result)
3436 }
3437
3438 fn jit_compile_integer_expr(&mut self, expr: &Ast, name: &str) -> Result<IntValue<'ctx>> {
3439 match &expr.kind {
3440 AstKind::Integer(value) => Ok(self.int_type.const_int(*value as u64, true)),
3441 AstKind::Number(value) => {
3442 if value.fract() != 0.0 {
3443 return Err(anyhow!(
3444 "non-integer value '{}' in integer expression",
3445 value
3446 ));
3447 }
3448 Ok(self.int_type.const_int(*value as u64, true))
3449 }
3450 AstKind::Name(iname) => {
3451 if iname.name == "N" {
3452 Ok(self
3453 .build_load(self.int_type, *self.get_param("model_index"), name)?
3454 .into_int_value())
3455 } else {
3456 Err(anyhow!(
3457 "unsupported name '{}' in integer expression",
3458 iname.name
3459 ))
3460 }
3461 }
3462 AstKind::Monop(monop) => {
3463 let child = self.jit_compile_integer_expr(monop.child.as_ref(), name)?;
3464 match monop.op {
3465 '+' => Ok(child),
3466 '-' => self.builder.build_int_neg(child, name).map_err(Into::into),
3467 _ => Err(anyhow!("unknown integer unary op '{}'", monop.op)),
3468 }
3469 }
3470 AstKind::Binop(binop) => {
3471 let lhs = self.jit_compile_integer_expr(binop.left.as_ref(), name)?;
3472 let rhs = self.jit_compile_integer_expr(binop.right.as_ref(), name)?;
3473 match binop.op {
3474 '+' => self
3475 .builder
3476 .build_int_add(lhs, rhs, name)
3477 .map_err(Into::into),
3478 '-' => self
3479 .builder
3480 .build_int_sub(lhs, rhs, name)
3481 .map_err(Into::into),
3482 '*' => self
3483 .builder
3484 .build_int_mul(lhs, rhs, name)
3485 .map_err(Into::into),
3486 '/' => self
3487 .builder
3488 .build_int_signed_div(lhs, rhs, name)
3489 .map_err(Into::into),
3490 '%' => self
3491 .builder
3492 .build_int_signed_rem(lhs, rhs, name)
3493 .map_err(Into::into),
3494 _ => Err(anyhow!("unknown integer binary op '{}'", binop.op)),
3495 }
3496 }
3497 _ => Err(anyhow!("unsupported integer expression '{}'", expr)),
3498 }
3499 }
3500
3501 fn clear(&mut self) {
3502 self.variables.clear();
3503 self.fn_value_opt = None;
3505 self.tensor_ptr_opt = None;
3506 }
3507
3508 fn build_dep_call(
3509 &mut self,
3510 dep_fn: FunctionValue<'ctx>,
3511 call_name: &str,
3512 barrier_start: u64,
3513 total_barriers: u64,
3514 ) -> Result<()> {
3515 let t = self
3516 .build_load(self.real_type, *self.get_param("t"), "t")?
3517 .into_float_value();
3518 let u = *self.get_param("u");
3519 let data = *self.get_param("data");
3520 let thread_id = self
3521 .build_load(self.int_type, *self.get_param("thread_id"), "thread_id")?
3522 .into_int_value();
3523 let thread_dim = self
3524 .build_load(self.int_type, *self.get_param("thread_dim"), "thread_dim")?
3525 .into_int_value();
3526 let barrier_start = self.int_type.const_int(barrier_start, false);
3527 let total_barriers = self.int_type.const_int(total_barriers, false);
3528 self.builder.build_call(
3529 dep_fn,
3530 &[
3531 t.into(),
3532 u.into(),
3533 data.into(),
3534 thread_id.into(),
3535 thread_dim.into(),
3536 barrier_start.into(),
3537 total_barriers.into(),
3538 ],
3539 call_name,
3540 )?;
3541 Ok(())
3542 }
3543
3544 fn ensure_time_dep_fn<'m>(
3545 &mut self,
3546 model: &'m DiscreteModel,
3547 code: Option<&str>,
3548 ) -> Result<FunctionValue<'ctx>> {
3549 if let Some(function) = self.module.get_function("calc_time_dep") {
3550 return Ok(function);
3551 }
3552 self.compile_dep_defns(model, "calc_time_dep", model.time_dep_defns(), code)
3553 }
3554
3555 fn ensure_state_dep_fn<'m>(
3556 &mut self,
3557 model: &'m DiscreteModel,
3558 code: Option<&str>,
3559 ) -> Result<FunctionValue<'ctx>> {
3560 if let Some(function) = self.module.get_function("calc_state_dep") {
3561 return Ok(function);
3562 }
3563 self.compile_dep_defns(model, "calc_state_dep", model.state_dep_defns(), code)
3564 }
3565
3566 fn ensure_state_dep_post_f_fn<'m>(
3567 &mut self,
3568 model: &'m DiscreteModel,
3569 code: Option<&str>,
3570 ) -> Result<FunctionValue<'ctx>> {
3571 if let Some(function) = self.module.get_function("calc_state_dep_post_f") {
3572 return Ok(function);
3573 }
3574 self.compile_dep_defns(
3575 model,
3576 "calc_state_dep_post_f",
3577 model.state_dep_post_f_defns(),
3578 code,
3579 )
3580 }
3581
3582 fn function_arg_alloca(&mut self, name: &str, arg: BasicValueEnum<'ctx>) -> PointerValue<'ctx> {
3583 match arg {
3584 BasicValueEnum::PointerValue(v) => v,
3585 BasicValueEnum::FloatValue(v) => {
3586 let alloca = self
3587 .create_entry_block_builder()
3588 .build_alloca(arg.get_type(), name)
3589 .unwrap();
3590 self.builder.build_store(alloca, v).unwrap();
3591 alloca
3592 }
3593 BasicValueEnum::IntValue(v) => {
3594 let alloca = self
3595 .create_entry_block_builder()
3596 .build_alloca(arg.get_type(), name)
3597 .unwrap();
3598 self.builder.build_store(alloca, v).unwrap();
3599 alloca
3600 }
3601 _ => unreachable!(),
3602 }
3603 }
3604
3605 pub fn compile_set_u0<'m>(
3606 &mut self,
3607 model: &'m DiscreteModel,
3608 code: Option<&str>,
3609 ) -> Result<FunctionValue<'ctx>> {
3610 self.clear();
3611 let fn_arg_names = &["u0", "data", "thread_id", "thread_dim"];
3612 let function = self.add_function(
3613 "set_u0",
3614 fn_arg_names,
3615 &[
3616 self.real_ptr_type.into(),
3617 self.real_ptr_type.into(),
3618 self.int_type.into(),
3619 self.int_type.into(),
3620 ],
3621 None,
3622 false,
3623 );
3624
3625 let alias_id = Attribute::get_named_enum_kind_id("noalias");
3627 let noalign = self.context.create_enum_attribute(alias_id, 0);
3628 for i in &[0, 1] {
3629 function.add_attribute(AttributeLoc::Param(*i), noalign);
3630 }
3631
3632 let _basic_block = self.start_function(function, code);
3633
3634 for (i, arg) in function.get_param_iter().enumerate() {
3635 let name = fn_arg_names[i];
3636 let alloca = self.function_arg_alloca(name, arg);
3637 self.insert_param(name, alloca);
3638 }
3639
3640 self.insert_data(model);
3641 self.insert_indices();
3642
3643 let mut nbarriers = 0;
3644 let total_barriers = (model.input_dep_defns().len() + 1) as u64;
3645 let total_barriers_val = self.int_type.const_int(total_barriers, false);
3646 #[allow(clippy::explicit_counter_loop)]
3647 for a in model.input_dep_defns() {
3648 self.jit_compile_tensor(a, Some(*self.get_var(a)), code)?;
3649 let barrier_num = self.int_type.const_int(nbarriers + 1, false);
3650 self.jit_compile_call_barrier(barrier_num, total_barriers_val);
3651 nbarriers += 1;
3652 }
3653
3654 self.jit_compile_tensor(model.state(), Some(*self.get_param("u0")), code)?;
3655 let barrier_num = self.int_type.const_int(nbarriers + 1, false);
3656 self.jit_compile_call_barrier(barrier_num, total_barriers_val);
3657
3658 self.builder.build_return(None)?;
3659
3660 if function.verify(true) {
3661 Ok(function)
3662 } else {
3663 function.print_to_stderr();
3664 unsafe {
3665 function.delete();
3666 }
3667 Err(anyhow!("Invalid generated function."))
3668 }
3669 }
3670
3671 pub fn compile_calc_out<'m>(
3672 &mut self,
3673 model: &'m DiscreteModel,
3674 include_constants: bool,
3675 code: Option<&str>,
3676 ) -> Result<FunctionValue<'ctx>> {
3677 let time_dep_fn = self.ensure_time_dep_fn(model, code)?;
3678 let state_dep_fn = self.ensure_state_dep_fn(model, code)?;
3679 let state_dep_post_f_fn = self.ensure_state_dep_post_f_fn(model, code)?;
3680 self.clear();
3681 let fn_arg_names = &["t", "u", "data", "out", "thread_id", "thread_dim"];
3682 let function_name = if include_constants {
3683 "calc_out_full"
3684 } else {
3685 "calc_out"
3686 };
3687 let function = self.add_function(
3688 function_name,
3689 fn_arg_names,
3690 &[
3691 self.real_type.into(),
3692 self.real_ptr_type.into(),
3693 self.real_ptr_type.into(),
3694 self.real_ptr_type.into(),
3695 self.int_type.into(),
3696 self.int_type.into(),
3697 ],
3698 None,
3699 false,
3700 );
3701
3702 let alias_id = Attribute::get_named_enum_kind_id("noalias");
3704 let noalign = self.context.create_enum_attribute(alias_id, 0);
3705 for i in &[1, 2, 3] {
3706 function.add_attribute(AttributeLoc::Param(*i), noalign);
3707 }
3708
3709 let _basic_block = self.start_function(function, code);
3710
3711 for (i, arg) in function.get_param_iter().enumerate() {
3712 let name = fn_arg_names[i];
3713 let alloca = self.function_arg_alloca(name, arg);
3714 self.insert_param(name, alloca);
3715 }
3716
3717 self.insert_state(model.state());
3718 self.insert_data(model);
3719 self.insert_indices();
3720
3721 if let Some(out) = model.out() {
3727 let mut nbarriers = 0;
3728 let mut total_barriers = (model.time_dep_defns().len()
3729 + model.state_dep_defns().len()
3730 + model.state_dep_post_f_defns().len()
3731 + 1) as u64;
3732 if include_constants {
3733 total_barriers += model.input_dep_defns().len() as u64;
3734 }
3735 let total_barriers_val = self.int_type.const_int(total_barriers, false);
3736 if include_constants {
3737 for tensor in model.input_dep_defns() {
3739 self.jit_compile_tensor(tensor, Some(*self.get_var(tensor)), code)?;
3740 let barrier_num = self.int_type.const_int(nbarriers + 1, false);
3741 self.jit_compile_call_barrier(barrier_num, total_barriers_val);
3742 nbarriers += 1;
3743 }
3744 }
3745
3746 if !model.time_dep_defns().is_empty() {
3748 self.build_dep_call(time_dep_fn, "time_dep", nbarriers, total_barriers)?;
3749 nbarriers += model.time_dep_defns().len() as u64;
3750 }
3751
3752 if !model.state_dep_defns().is_empty() {
3754 self.build_dep_call(state_dep_fn, "state_dep", nbarriers, total_barriers)?;
3755 nbarriers += model.state_dep_defns().len() as u64;
3756 }
3757 if !model.state_dep_post_f_defns().is_empty() {
3758 self.build_dep_call(state_dep_post_f_fn, "state_dep", nbarriers, total_barriers)?;
3759 nbarriers += model.state_dep_post_f_defns().len() as u64;
3760 }
3761
3762 self.jit_compile_tensor(out, Some(*self.get_var(model.out().unwrap())), code)?;
3763 let barrier_num = self.int_type.const_int(nbarriers + 1, false);
3764 self.jit_compile_call_barrier(barrier_num, total_barriers_val);
3765 }
3766 self.builder.build_return(None)?;
3767
3768 if function.verify(true) {
3769 Ok(function)
3770 } else {
3771 function.print_to_stderr();
3772 unsafe {
3773 function.delete();
3774 }
3775 Err(anyhow!("Invalid generated function."))
3776 }
3777 }
3778
3779 fn compile_dep_defns<'m>(
3780 &mut self,
3781 model: &'m DiscreteModel,
3782 fn_name: &str,
3783 tensors: &[Tensor<'m>],
3784 code: Option<&str>,
3785 ) -> Result<FunctionValue<'ctx>> {
3786 self.clear();
3787 let fn_arg_names = &[
3788 "t",
3789 "u",
3790 "data",
3791 "thread_id",
3792 "thread_dim",
3793 "barrier_start",
3794 "total_barriers",
3795 ];
3796 let function = self.add_function(
3797 fn_name,
3798 fn_arg_names,
3799 &[
3800 self.real_type.into(),
3801 self.real_ptr_type.into(),
3802 self.real_ptr_type.into(),
3803 self.int_type.into(),
3804 self.int_type.into(),
3805 self.int_type.into(),
3806 self.int_type.into(),
3807 ],
3808 None,
3809 false,
3810 );
3811
3812 let alias_id = Attribute::get_named_enum_kind_id("noalias");
3814 let noalign = self.context.create_enum_attribute(alias_id, 0);
3815 for i in &[1, 2] {
3816 function.add_attribute(AttributeLoc::Param(*i), noalign);
3817 }
3818
3819 let _basic_block = self.start_function(function, code);
3820
3821 for (i, arg) in function.get_param_iter().enumerate() {
3822 let name = fn_arg_names[i];
3823 let alloca = self.function_arg_alloca(name, arg);
3824 self.insert_param(name, alloca);
3825 }
3826
3827 self.insert_state(model.state());
3828 self.insert_data(model);
3829 self.insert_indices();
3830
3831 let barrier_start = self
3832 .build_load(
3833 self.int_type,
3834 *self.get_param("barrier_start"),
3835 "barrier_start",
3836 )?
3837 .into_int_value();
3838 let total_barriers = self
3839 .build_load(
3840 self.int_type,
3841 *self.get_param("total_barriers"),
3842 "total_barriers",
3843 )?
3844 .into_int_value();
3845 let one = self.int_type.const_int(1, false);
3846
3847 for (index, tensor) in tensors.iter().enumerate() {
3848 self.jit_compile_tensor(tensor, Some(*self.get_var(tensor)), code)?;
3849 let index = self.int_type.const_int(index as u64, false);
3850 let barrier_num_base =
3851 self.builder
3852 .build_int_add(barrier_start, index, "barrier_num_base")?;
3853 let barrier_num = self
3854 .builder
3855 .build_int_add(barrier_num_base, one, "barrier_num")?;
3856 self.jit_compile_call_barrier(barrier_num, total_barriers);
3857 }
3858
3859 self.builder.build_return(None)?;
3860
3861 if function.verify(true) {
3862 Ok(function)
3863 } else {
3864 function.print_to_stderr();
3865 unsafe {
3866 function.delete();
3867 }
3868 Err(anyhow!("Invalid generated function."))
3869 }
3870 }
3871
3872 pub fn compile_calc_stop<'m>(
3873 &mut self,
3874 model: &'m DiscreteModel,
3875 include_constants: bool,
3876 code: Option<&str>,
3877 ) -> Result<FunctionValue<'ctx>> {
3878 let time_dep_fn = self.ensure_time_dep_fn(model, code)?;
3879 let state_dep_fn = self.ensure_state_dep_fn(model, code)?;
3880 let state_dep_post_f_fn = self.ensure_state_dep_post_f_fn(model, code)?;
3881 self.clear();
3882 let fn_arg_names = &["t", "u", "data", "root", "thread_id", "thread_dim"];
3883 let function_name = if include_constants {
3884 "calc_stop_full"
3885 } else {
3886 "calc_stop"
3887 };
3888 let function = self.add_function(
3889 function_name,
3890 fn_arg_names,
3891 &[
3892 self.real_type.into(),
3893 self.real_ptr_type.into(),
3894 self.real_ptr_type.into(),
3895 self.real_ptr_type.into(),
3896 self.int_type.into(),
3897 self.int_type.into(),
3898 ],
3899 None,
3900 false,
3901 );
3902
3903 let alias_id = Attribute::get_named_enum_kind_id("noalias");
3905 let noalign = self.context.create_enum_attribute(alias_id, 0);
3906 for i in &[1, 2, 3] {
3907 function.add_attribute(AttributeLoc::Param(*i), noalign);
3908 }
3909
3910 let _basic_block = self.start_function(function, code);
3911
3912 for (i, arg) in function.get_param_iter().enumerate() {
3913 let name = fn_arg_names[i];
3914 let alloca = self.function_arg_alloca(name, arg);
3915 self.insert_param(name, alloca);
3916 }
3917
3918 self.insert_state(model.state());
3919 self.insert_data(model);
3920 self.insert_indices();
3921
3922 if let Some(stop) = model.stop() {
3923 let mut nbarriers = 0;
3925 let mut total_barriers = (model.time_dep_defns().len()
3926 + model.state_dep_defns().len()
3927 + model.state_dep_post_f_defns().len()
3928 + 1) as u64;
3929 if include_constants {
3930 total_barriers += model.input_dep_defns().len() as u64;
3931 }
3932 let total_barriers_val = self.int_type.const_int(total_barriers, false);
3933 if include_constants {
3934 for tensor in model.input_dep_defns() {
3936 self.jit_compile_tensor(tensor, Some(*self.get_var(tensor)), code)?;
3937 let barrier_num = self.int_type.const_int(nbarriers + 1, false);
3938 self.jit_compile_call_barrier(barrier_num, total_barriers_val);
3939 nbarriers += 1;
3940 }
3941 }
3942 if !model.time_dep_defns().is_empty() {
3943 self.build_dep_call(time_dep_fn, "time_dep", nbarriers, total_barriers)?;
3944 nbarriers += model.time_dep_defns().len() as u64;
3945 }
3946
3947 if !model.state_dep_defns().is_empty() {
3949 self.build_dep_call(state_dep_fn, "state_dep", nbarriers, total_barriers)?;
3950 nbarriers += model.state_dep_defns().len() as u64;
3951 }
3952 if !model.state_dep_post_f_defns().is_empty() {
3953 self.build_dep_call(state_dep_post_f_fn, "state_dep", nbarriers, total_barriers)?;
3954 nbarriers += model.state_dep_post_f_defns().len() as u64;
3955 }
3956
3957 let res_ptr = self.get_param("root");
3958 self.jit_compile_tensor(stop, Some(*res_ptr), code)?;
3959 let barrier_num = self.int_type.const_int(nbarriers + 1, false);
3960 self.jit_compile_call_barrier(barrier_num, total_barriers_val);
3961 }
3962 self.builder.build_return(None)?;
3963
3964 if function.verify(true) {
3965 Ok(function)
3966 } else {
3967 function.print_to_stderr();
3968 unsafe {
3969 function.delete();
3970 }
3971 Err(anyhow!("Invalid generated function."))
3972 }
3973 }
3974
3975 pub fn compile_reset<'m>(
3976 &mut self,
3977 model: &'m DiscreteModel,
3978 include_constants: bool,
3979 code: Option<&str>,
3980 ) -> Result<FunctionValue<'ctx>> {
3981 let time_dep_fn = self.ensure_time_dep_fn(model, code)?;
3982 let state_dep_fn = self.ensure_state_dep_fn(model, code)?;
3983 let state_dep_post_f_fn = self.ensure_state_dep_post_f_fn(model, code)?;
3984 self.clear();
3985 let fn_arg_names = &["t", "u", "data", "reset", "thread_id", "thread_dim"];
3986 let function_name = if include_constants {
3987 "reset_full"
3988 } else {
3989 "reset"
3990 };
3991 let function = self.add_function(
3992 function_name,
3993 fn_arg_names,
3994 &[
3995 self.real_type.into(),
3996 self.real_ptr_type.into(),
3997 self.real_ptr_type.into(),
3998 self.real_ptr_type.into(),
3999 self.int_type.into(),
4000 self.int_type.into(),
4001 ],
4002 None,
4003 false,
4004 );
4005
4006 let alias_id = Attribute::get_named_enum_kind_id("noalias");
4007 let noalign = self.context.create_enum_attribute(alias_id, 0);
4008 for i in &[1, 2, 3] {
4009 function.add_attribute(AttributeLoc::Param(*i), noalign);
4010 }
4011
4012 let _basic_block = self.start_function(function, code);
4013
4014 for (i, arg) in function.get_param_iter().enumerate() {
4015 let name = fn_arg_names[i];
4016 let alloca = self.function_arg_alloca(name, arg);
4017 self.insert_param(name, alloca);
4018 }
4019
4020 self.insert_state(model.state());
4021 self.insert_data(model);
4022 self.insert_indices();
4023
4024 if let Some(reset) = model.reset() {
4025 let mut nbarriers = 0;
4026 let mut total_barriers = (model.time_dep_defns().len()
4027 + model.state_dep_defns().len()
4028 + model.state_dep_post_f_defns().len()
4029 + 1) as u64;
4030 if include_constants {
4031 total_barriers += model.input_dep_defns().len() as u64;
4032 }
4033 let total_barriers_val = self.int_type.const_int(total_barriers, false);
4034 if include_constants {
4035 for tensor in model.input_dep_defns() {
4037 self.jit_compile_tensor(tensor, Some(*self.get_var(tensor)), code)?;
4038 let barrier_num = self.int_type.const_int(nbarriers + 1, false);
4039 self.jit_compile_call_barrier(barrier_num, total_barriers_val);
4040 nbarriers += 1;
4041 }
4042 }
4043 if !model.time_dep_defns().is_empty() {
4044 self.build_dep_call(time_dep_fn, "time_dep", nbarriers, total_barriers)?;
4045 nbarriers += model.time_dep_defns().len() as u64;
4046 }
4047
4048 if !model.state_dep_defns().is_empty() {
4049 self.build_dep_call(state_dep_fn, "state_dep", nbarriers, total_barriers)?;
4050 nbarriers += model.state_dep_defns().len() as u64;
4051 }
4052 if !model.state_dep_post_f_defns().is_empty() {
4053 self.build_dep_call(state_dep_post_f_fn, "state_dep", nbarriers, total_barriers)?;
4054 nbarriers += model.state_dep_post_f_defns().len() as u64;
4055 }
4056
4057 let res_ptr = self.get_param("reset");
4058 self.jit_compile_tensor(reset, Some(*res_ptr), code)?;
4059 let barrier_num = self.int_type.const_int(nbarriers + 1, false);
4060 self.jit_compile_call_barrier(barrier_num, total_barriers_val);
4061 }
4062 self.builder.build_return(None)?;
4063
4064 if function.verify(true) {
4065 Ok(function)
4066 } else {
4067 function.print_to_stderr();
4068 unsafe {
4069 function.delete();
4070 }
4071 Err(anyhow!("Invalid generated function."))
4072 }
4073 }
4074
4075 pub fn compile_rhs<'m>(
4076 &mut self,
4077 model: &'m DiscreteModel,
4078 include_constants: bool,
4079 code: Option<&str>,
4080 ) -> Result<FunctionValue<'ctx>> {
4081 let time_dep_fn = self.ensure_time_dep_fn(model, code)?;
4082 let state_dep_fn = self.ensure_state_dep_fn(model, code)?;
4083 self.clear();
4084 let fn_arg_names = &["t", "u", "data", "rr", "thread_id", "thread_dim"];
4085 let function_name = if include_constants { "rhs_full" } else { "rhs" };
4086 let function = self.add_function(
4087 function_name,
4088 fn_arg_names,
4089 &[
4090 self.real_type.into(),
4091 self.real_ptr_type.into(),
4092 self.real_ptr_type.into(),
4093 self.real_ptr_type.into(),
4094 self.int_type.into(),
4095 self.int_type.into(),
4096 ],
4097 None,
4098 false,
4099 );
4100
4101 let alias_id = Attribute::get_named_enum_kind_id("noalias");
4103 let noalign = self.context.create_enum_attribute(alias_id, 0);
4104 for i in &[1, 2, 3] {
4105 function.add_attribute(AttributeLoc::Param(*i), noalign);
4106 }
4107
4108 let _basic_block = self.start_function(function, code);
4109
4110 for (i, arg) in function.get_param_iter().enumerate() {
4111 let name = fn_arg_names[i];
4112 let alloca = self.function_arg_alloca(name, arg);
4113 self.insert_param(name, alloca);
4114 }
4115
4116 self.insert_state(model.state());
4117 self.insert_data(model);
4118 self.insert_indices();
4119
4120 let mut nbarriers = 0;
4121 let mut total_barriers =
4122 (model.time_dep_defns().len() + model.state_dep_defns().len() + 1) as u64;
4123 if include_constants {
4124 total_barriers += model.input_dep_defns().len() as u64;
4125 for tensor in model.input_dep_defns() {
4127 self.jit_compile_tensor(tensor, Some(*self.get_var(tensor)), code)?;
4128 let barrier_num = self.int_type.const_int(nbarriers + 1, false);
4129 let total_barriers_val = self.int_type.const_int(total_barriers, false);
4130 self.jit_compile_call_barrier(barrier_num, total_barriers_val);
4131 nbarriers += 1;
4132 }
4133 }
4134
4135 if !model.time_dep_defns().is_empty() {
4137 self.build_dep_call(time_dep_fn, "time_dep", nbarriers, total_barriers)?;
4138 nbarriers += model.time_dep_defns().len() as u64;
4139 }
4140
4141 if !model.state_dep_defns().is_empty() {
4142 self.build_dep_call(state_dep_fn, "state_dep", nbarriers, total_barriers)?;
4143 nbarriers += model.state_dep_defns().len() as u64;
4144 }
4145
4146 let res_ptr = self.get_param("rr");
4148 self.jit_compile_tensor(model.rhs(), Some(*res_ptr), code)?;
4149 let total_barriers_val = self.int_type.const_int(total_barriers, false);
4150 let barrier_num = self.int_type.const_int(nbarriers + 1, false);
4151 self.jit_compile_call_barrier(barrier_num, total_barriers_val);
4152
4153 self.builder.build_return(None)?;
4154
4155 if function.verify(true) {
4156 Ok(function)
4157 } else {
4158 function.print_to_stderr();
4159 unsafe {
4160 function.delete();
4161 }
4162 Err(anyhow!("Invalid generated function."))
4163 }
4164 }
4165
4166 pub fn compile_mass<'m>(
4167 &mut self,
4168 model: &'m DiscreteModel,
4169 code: Option<&str>,
4170 ) -> Result<FunctionValue<'ctx>> {
4171 self.clear();
4172 let fn_arg_names = &["t", "dudt", "data", "rr", "thread_id", "thread_dim"];
4173 let function = self.add_function(
4174 "mass",
4175 fn_arg_names,
4176 &[
4177 self.real_type.into(),
4178 self.real_ptr_type.into(),
4179 self.real_ptr_type.into(),
4180 self.real_ptr_type.into(),
4181 self.int_type.into(),
4182 self.int_type.into(),
4183 ],
4184 None,
4185 false,
4186 );
4187
4188 let alias_id = Attribute::get_named_enum_kind_id("noalias");
4190 let noalign = self.context.create_enum_attribute(alias_id, 0);
4191 for i in &[1, 2, 3] {
4192 function.add_attribute(AttributeLoc::Param(*i), noalign);
4193 }
4194
4195 let _basic_block = self.start_function(function, code);
4196
4197 for (i, arg) in function.get_param_iter().enumerate() {
4198 let name = fn_arg_names[i];
4199 let alloca = self.function_arg_alloca(name, arg);
4200 self.insert_param(name, alloca);
4201 }
4202
4203 if model.state_dot().is_some() && model.lhs().is_some() {
4205 let state_dot = model.state_dot().unwrap();
4206 let lhs = model.lhs().unwrap();
4207
4208 self.insert_dot_state(state_dot);
4209 self.insert_data(model);
4210 self.insert_indices();
4211
4212 let mut nbarriers = 0;
4214 let total_barriers =
4215 (model.time_dep_defns().len() + model.dstate_dep_defns().len() + 1) as u64;
4216 let total_barriers_val = self.int_type.const_int(total_barriers, false);
4217 for tensor in model.time_dep_defns() {
4218 self.jit_compile_tensor(tensor, Some(*self.get_var(tensor)), code)?;
4219 let barrier_num = self.int_type.const_int(nbarriers + 1, false);
4220 self.jit_compile_call_barrier(barrier_num, total_barriers_val);
4221 nbarriers += 1;
4222 }
4223
4224 for a in model.dstate_dep_defns() {
4225 self.jit_compile_tensor(a, Some(*self.get_var(a)), code)?;
4226 let barrier_num = self.int_type.const_int(nbarriers + 1, false);
4227 self.jit_compile_call_barrier(barrier_num, total_barriers_val);
4228 nbarriers += 1;
4229 }
4230
4231 let res_ptr = self.get_param("rr");
4233 self.jit_compile_tensor(lhs, Some(*res_ptr), code)?;
4234 let barrier_num = self.int_type.const_int(nbarriers + 1, false);
4235 self.jit_compile_call_barrier(barrier_num, total_barriers_val);
4236 }
4237
4238 self.builder.build_return(None)?;
4239
4240 if function.verify(true) {
4241 Ok(function)
4242 } else {
4243 function.print_to_stderr();
4244 unsafe {
4245 function.delete();
4246 }
4247 Err(anyhow!("Invalid generated function."))
4248 }
4249 }
4250
4251 pub fn compile_gradient(
4252 &mut self,
4253 original_function: FunctionValue<'ctx>,
4254 args_type: &[CompileGradientArgType],
4255 mode: CompileMode,
4256 fn_name: &str,
4257 ) -> Result<FunctionValue<'ctx>> {
4258 self.clear();
4259
4260 let mut fn_type: Vec<BasicMetadataTypeEnum> = Vec::new();
4262
4263 let orig_fn_type_ptr = Self::fn_pointer_type(self.context, original_function.get_type());
4264
4265 let mut enzyme_fn_type: Vec<BasicMetadataTypeEnum> = vec![orig_fn_type_ptr.into()];
4266 let mut start_param_index: Vec<u32> = Vec::new();
4267 let mut ptr_arg_indices: Vec<u32> = Vec::new();
4268 for (i, arg) in original_function.get_param_iter().enumerate() {
4269 let start_index = u32::try_from(fn_type.len()).unwrap();
4270 start_param_index.push(start_index);
4271 let arg_type = arg.get_type();
4272 fn_type.push(arg_type.into());
4273
4274 enzyme_fn_type.push(self.int_type.into());
4276 enzyme_fn_type.push(arg.get_type().into());
4277
4278 if arg_type.is_pointer_type() {
4279 ptr_arg_indices.push(start_index);
4280 }
4281
4282 match args_type[i] {
4283 CompileGradientArgType::Dup | CompileGradientArgType::DupNoNeed => {
4284 fn_type.push(arg.get_type().into());
4285 enzyme_fn_type.push(arg.get_type().into());
4286 if arg_type.is_pointer_type() {
4287 ptr_arg_indices.push(start_index + 1);
4288 }
4289 }
4290 CompileGradientArgType::Const => {}
4291 }
4292 }
4293 let fn_arg_names = fn_type
4294 .iter()
4295 .enumerate()
4296 .map(|(i, _)| format!("arg{}", i))
4297 .collect::<Vec<String>>();
4298 let fn_arg_names_ref = fn_arg_names
4299 .iter()
4300 .map(|s| s.as_str())
4301 .collect::<Vec<&str>>();
4302 let function = self.add_function(fn_name, &fn_arg_names_ref, &fn_type, None, false);
4303
4304 let alias_id = Attribute::get_named_enum_kind_id("noalias");
4306 let noalign = self.context.create_enum_attribute(alias_id, 0);
4307 for i in ptr_arg_indices {
4308 function.add_attribute(AttributeLoc::Param(i), noalign);
4309 }
4310
4311 let _basic_block = self.start_function(function, None);
4312
4313 let mut enzyme_fn_args: Vec<BasicMetadataValueEnum> = Vec::new();
4314 let mut input_activity = Vec::new();
4315 let mut arg_trees = Vec::new();
4316 for (i, arg) in original_function.get_param_iter().enumerate() {
4317 let param_index = start_param_index[i];
4318 let fn_arg = function.get_nth_param(param_index).unwrap();
4319
4320 let concrete_type = match arg.get_type() {
4323 BasicTypeEnum::PointerType(_t) => CConcreteType_DT_Pointer,
4324 BasicTypeEnum::FloatType(_t) => match self.diffsl_real_type {
4325 RealType::F32 => CConcreteType_DT_Float,
4326 RealType::F64 => CConcreteType_DT_Double,
4327 },
4328 BasicTypeEnum::IntType(_) => CConcreteType_DT_Integer,
4329 _ => panic!("unsupported type"),
4330 };
4331 let new_tree = unsafe {
4332 EnzymeNewTypeTreeCT(
4333 concrete_type,
4334 self.context.as_ctx_ref() as *mut LLVMOpaqueContext,
4335 )
4336 };
4337 unsafe { EnzymeTypeTreeOnlyEq(new_tree, -1) };
4338
4339 if concrete_type == CConcreteType_DT_Pointer {
4341 let inner_concrete_type = match self.diffsl_real_type {
4342 RealType::F32 => CConcreteType_DT_Float,
4343 RealType::F64 => CConcreteType_DT_Double,
4344 };
4345 let inner_new_tree = unsafe {
4346 EnzymeNewTypeTreeCT(
4347 inner_concrete_type,
4348 self.context.as_ctx_ref() as *mut LLVMOpaqueContext,
4349 )
4350 };
4351 unsafe { EnzymeTypeTreeOnlyEq(inner_new_tree, -1) };
4352 unsafe { EnzymeTypeTreeOnlyEq(inner_new_tree, -1) };
4353 unsafe { EnzymeMergeTypeTree(new_tree, inner_new_tree) };
4354 }
4355
4356 arg_trees.push(new_tree);
4357 match args_type[i] {
4358 CompileGradientArgType::Dup => {
4359 enzyme_fn_args.push(fn_arg.into());
4361
4362 let fn_darg = function.get_nth_param(param_index + 1).unwrap();
4364 enzyme_fn_args.push(fn_darg.into());
4365
4366 input_activity.push(CDIFFE_TYPE_DFT_DUP_ARG);
4367 }
4368 CompileGradientArgType::DupNoNeed => {
4369 enzyme_fn_args.push(fn_arg.into());
4371
4372 let fn_darg = function.get_nth_param(param_index + 1).unwrap();
4374 enzyme_fn_args.push(fn_darg.into());
4375
4376 input_activity.push(CDIFFE_TYPE_DFT_DUP_NONEED);
4377 }
4378 CompileGradientArgType::Const => {
4379 enzyme_fn_args.push(fn_arg.into());
4381
4382 input_activity.push(CDIFFE_TYPE_DFT_CONSTANT);
4383 }
4384 }
4385 }
4386 let ret_primary_ret = false;
4388 let diff_ret = false;
4389 let ret_activity = CDIFFE_TYPE_DFT_CONSTANT;
4390 let ret_tree = unsafe {
4391 EnzymeNewTypeTreeCT(
4392 CConcreteType_DT_Anything,
4393 self.context.as_ctx_ref() as *mut LLVMOpaqueContext,
4394 )
4395 };
4396
4397 let fnc_opt_base = true;
4399 let logic_ref: EnzymeLogicRef = unsafe { CreateEnzymeLogic(fnc_opt_base as u8) };
4400
4401 let kv_tmp = IntList {
4402 data: std::ptr::null_mut(),
4403 size: 0,
4404 };
4405 let mut known_values = vec![kv_tmp; input_activity.len()];
4406
4407 let fn_type_info = CFnTypeInfo {
4408 Arguments: arg_trees.as_mut_ptr(),
4409 Return: ret_tree,
4410 KnownValues: known_values.as_mut_ptr(),
4411 };
4412
4413 let type_analysis: EnzymeTypeAnalysisRef =
4414 unsafe { CreateTypeAnalysis(logic_ref, std::ptr::null_mut(), std::ptr::null_mut(), 0) };
4415
4416 let mut args_uncacheable = vec![0; arg_trees.len()];
4417
4418 let enzyme_function = match mode {
4419 CompileMode::Forward | CompileMode::ForwardSens => unsafe {
4420 EnzymeCreateForwardDiff(
4421 logic_ref, std::ptr::null_mut(),
4423 std::ptr::null_mut(),
4424 original_function.as_value_ref(),
4425 ret_activity, input_activity.as_mut_ptr(),
4427 input_activity.len(), type_analysis, ret_primary_ret as u8,
4430 CDerivativeMode_DEM_ForwardMode, 1, 0, 0, 1, std::ptr::null_mut(), fn_type_info, 1, args_uncacheable.as_mut_ptr(), args_uncacheable.len(), std::ptr::null_mut(), )
4442 },
4443 CompileMode::Reverse | CompileMode::ReverseSens => {
4444 let mut call_enzyme = || unsafe {
4445 EnzymeCreatePrimalAndGradient(
4446 logic_ref,
4447 std::ptr::null_mut(),
4448 std::ptr::null_mut(),
4449 original_function.as_value_ref(),
4450 ret_activity,
4451 input_activity.as_mut_ptr(),
4452 input_activity.len(),
4453 type_analysis,
4454 ret_primary_ret as u8,
4455 diff_ret as u8,
4456 CDerivativeMode_DEM_ReverseModeCombined,
4457 0,
4458 0, 1,
4460 1,
4461 std::ptr::null_mut(),
4462 0, fn_type_info,
4464 0, args_uncacheable.as_mut_ptr(),
4466 args_uncacheable.len(),
4467 std::ptr::null_mut(),
4468 if self.threaded { 1 } else { 0 }, )
4470 };
4471 if self.threaded {
4472 let _lock = my_mutex.lock().unwrap();
4474 let barrier_string = CString::new("barrier").unwrap();
4475 unsafe {
4476 EnzymeRegisterCallHandler(
4477 barrier_string.as_ptr(),
4478 Some(fwd_handler),
4479 Some(rev_handler),
4480 )
4481 };
4482 let ret = call_enzyme();
4483 unsafe { EnzymeRegisterCallHandler(barrier_string.as_ptr(), None, None) };
4485 ret
4486 } else {
4487 call_enzyme()
4488 }
4489 }
4490 };
4491
4492 unsafe { FreeEnzymeLogic(logic_ref) };
4494 unsafe { FreeTypeAnalysis(type_analysis) };
4495 unsafe { EnzymeFreeTypeTree(ret_tree) };
4496 for tree in arg_trees {
4497 unsafe { EnzymeFreeTypeTree(tree) };
4498 }
4499
4500 let enzyme_function =
4502 unsafe { FunctionValue::new(enzyme_function as LLVMValueRef) }.unwrap();
4503 self.builder
4504 .build_call(enzyme_function, enzyme_fn_args.as_slice(), "enzyme_call")?;
4505
4506 self.builder.build_return(None)?;
4508
4509 if function.verify(true) {
4510 Ok(function)
4511 } else {
4512 function.print_to_stderr();
4513 enzyme_function.print_to_stderr();
4514 unsafe {
4515 function.delete();
4516 }
4517 Err(anyhow!("Invalid generated function."))
4518 }
4519 }
4520
4521 pub fn compile_get_dims(&mut self, model: &DiscreteModel) -> Result<FunctionValue<'ctx>> {
4522 self.clear();
4523 let fn_arg_names = &[
4524 "states",
4525 "inputs",
4526 "outputs",
4527 "data",
4528 "stop",
4529 "has_mass",
4530 "has_reset",
4531 ];
4532 let function = self.add_function(
4533 "get_dims",
4534 fn_arg_names,
4535 &[
4536 self.int_ptr_type.into(),
4537 self.int_ptr_type.into(),
4538 self.int_ptr_type.into(),
4539 self.int_ptr_type.into(),
4540 self.int_ptr_type.into(),
4541 self.int_ptr_type.into(),
4542 self.int_ptr_type.into(),
4543 ],
4544 None,
4545 false,
4546 );
4547 let _block = self.start_function(function, None);
4548
4549 for (i, arg) in function.get_param_iter().enumerate() {
4550 let name = fn_arg_names[i];
4551 let alloca = self.function_arg_alloca(name, arg);
4552 self.insert_param(name, alloca);
4553 }
4554
4555 self.insert_indices();
4556
4557 let number_of_states = model.state().nnz() as u64;
4558 let number_of_inputs = model.input().map(|inp| inp.nnz()).unwrap_or(0) as u64;
4559 let number_of_outputs = match model.out() {
4560 Some(out) => out.nnz() as u64,
4561 None => 0,
4562 };
4563 let number_of_stop = if let Some(stop) = model.stop() {
4564 stop.nnz() as u64
4565 } else {
4566 0
4567 };
4568 let has_mass = match model.lhs().is_some() {
4569 true => 1u64,
4570 false => 0u64,
4571 };
4572 let has_reset = match model.reset().is_some() {
4573 true => 1u64,
4574 false => 0u64,
4575 };
4576 let data_len = self.layout.data().len() as u64;
4577 self.builder.build_store(
4578 *self.get_param("states"),
4579 self.int_type.const_int(number_of_states, false),
4580 )?;
4581 self.builder.build_store(
4582 *self.get_param("inputs"),
4583 self.int_type.const_int(number_of_inputs, false),
4584 )?;
4585 self.builder.build_store(
4586 *self.get_param("outputs"),
4587 self.int_type.const_int(number_of_outputs, false),
4588 )?;
4589 self.builder.build_store(
4590 *self.get_param("data"),
4591 self.int_type.const_int(data_len, false),
4592 )?;
4593 self.builder.build_store(
4594 *self.get_param("stop"),
4595 self.int_type.const_int(number_of_stop, false),
4596 )?;
4597 self.builder.build_store(
4598 *self.get_param("has_mass"),
4599 self.int_type.const_int(has_mass, false),
4600 )?;
4601 self.builder.build_store(
4602 *self.get_param("has_reset"),
4603 self.int_type.const_int(has_reset, false),
4604 )?;
4605 self.builder.build_return(None)?;
4606
4607 if function.verify(true) {
4608 Ok(function)
4609 } else {
4610 function.print_to_stderr();
4611 unsafe {
4612 function.delete();
4613 }
4614 Err(anyhow!("Invalid generated function."))
4615 }
4616 }
4617
4618 pub fn compile_get_tensor(
4619 &mut self,
4620 model: &DiscreteModel,
4621 name: &str,
4622 ) -> Result<FunctionValue<'ctx>> {
4623 self.clear();
4624 let real_ptr_ptr_type = Self::pointer_type(self.context, self.real_ptr_type.into());
4625 let function_name = format!("get_tensor_{name}");
4626 let fn_arg_names = &["data", "tensor_data", "tensor_size"];
4627 let function = self.add_function(
4628 function_name.as_str(),
4629 fn_arg_names,
4630 &[
4631 self.real_ptr_type.into(),
4632 real_ptr_ptr_type.into(),
4633 self.int_ptr_type.into(),
4634 ],
4635 None,
4636 false,
4637 );
4638 let _basic_block = self.start_function(function, None);
4639
4640 for (i, arg) in function.get_param_iter().enumerate() {
4641 let name = fn_arg_names[i];
4642 let alloca = self.function_arg_alloca(name, arg);
4643 self.insert_param(name, alloca);
4644 }
4645
4646 self.insert_data(model);
4647 let ptr = self.get_param(name);
4648 let tensor_size = self.layout.get_layout(name).unwrap().nnz() as u64;
4649 let tensor_size_value = self.int_type.const_int(tensor_size, false);
4650 self.builder
4651 .build_store(*self.get_param("tensor_data"), ptr.as_basic_value_enum())?;
4652 self.builder
4653 .build_store(*self.get_param("tensor_size"), tensor_size_value)?;
4654 self.builder.build_return(None)?;
4655
4656 if function.verify(true) {
4657 Ok(function)
4658 } else {
4659 function.print_to_stderr();
4660 unsafe {
4661 function.delete();
4662 }
4663 Err(anyhow!("Invalid generated function."))
4664 }
4665 }
4666
4667 pub fn compile_get_constant(
4668 &mut self,
4669 model: &DiscreteModel,
4670 name: &str,
4671 ) -> Result<FunctionValue<'ctx>> {
4672 self.clear();
4673 let real_ptr_ptr_type = Self::pointer_type(self.context, self.real_ptr_type.into());
4674 let function_name = format!("get_constant_{name}");
4675 let fn_arg_names = &["tensor_data", "tensor_size"];
4676 let function = self.add_function(
4677 function_name.as_str(),
4678 fn_arg_names,
4679 &[real_ptr_ptr_type.into(), self.int_ptr_type.into()],
4680 None,
4681 false,
4682 );
4683 let _basic_block = self.start_function(function, None);
4684
4685 for (i, arg) in function.get_param_iter().enumerate() {
4686 let name = fn_arg_names[i];
4687 let alloca = self.function_arg_alloca(name, arg);
4688 self.insert_param(name, alloca);
4689 }
4690
4691 self.insert_constants(model);
4692 let ptr = self.get_param(name);
4693 let tensor_size = self.layout.get_layout(name).unwrap().nnz() as u64;
4694 let tensor_size_value = self.int_type.const_int(tensor_size, false);
4695 self.builder
4696 .build_store(*self.get_param("tensor_data"), ptr.as_basic_value_enum())?;
4697 self.builder
4698 .build_store(*self.get_param("tensor_size"), tensor_size_value)?;
4699 self.builder.build_return(None)?;
4700
4701 if function.verify(true) {
4702 Ok(function)
4703 } else {
4704 function.print_to_stderr();
4705 unsafe {
4706 function.delete();
4707 }
4708 Err(anyhow!("Invalid generated function."))
4709 }
4710 }
4711
4712 pub fn compile_inputs(
4713 &mut self,
4714 model: &DiscreteModel,
4715 is_get: bool,
4716 ) -> Result<FunctionValue<'ctx>> {
4717 self.clear();
4718 let function_name = if is_get { "get_inputs" } else { "set_inputs" };
4719 let fn_arg_names: &[&str] = if is_get {
4720 &["inputs", "data"]
4721 } else {
4722 &["inputs", "data", "model_index"]
4723 };
4724 let fn_arg_types: &[BasicMetadataTypeEnum<'ctx>] = if is_get {
4725 &[self.real_ptr_type.into(), self.real_ptr_type.into()]
4726 } else {
4727 &[
4728 self.real_ptr_type.into(),
4729 self.real_ptr_type.into(),
4730 self.int_type.into(),
4731 ]
4732 };
4733 let function = self.add_function(function_name, fn_arg_names, fn_arg_types, None, false);
4734 let block = self.start_function(function, None);
4735
4736 for (i, arg) in function.get_param_iter().enumerate() {
4737 let name = fn_arg_names[i];
4738 let alloca = self.function_arg_alloca(name, arg);
4739 self.insert_param(name, alloca);
4740 }
4741
4742 if !is_get {
4743 let model_index = self
4744 .build_load(self.int_type, *self.get_param("model_index"), "model_index")?
4745 .into_int_value();
4746 self.builder
4747 .build_store(self.globals.model_index.as_pointer_value(), model_index)?;
4748 }
4749
4750 if let Some(input) = model.input() {
4751 let name = input.name();
4752 self.insert_tensor(input, false);
4753 let ptr = self.get_var(input);
4754 let inputs_start_index = self.int_type.const_int(0, false);
4756 let start_index = self.int_type.const_int(0, false);
4757 let end_index = self
4758 .int_type
4759 .const_int(input.nnz().try_into().unwrap(), false);
4760
4761 let input_block = self.context.append_basic_block(function, name);
4762 self.builder.build_unconditional_branch(input_block)?;
4763 self.builder.position_at_end(input_block);
4764 let index = self.builder.build_phi(self.int_type, "i")?;
4765 index.add_incoming(&[(&start_index, block)]);
4766
4767 let curr_input_index = index.as_basic_value().into_int_value();
4769 let input_ptr =
4770 Self::get_ptr_to_index(&self.builder, self.real_type, ptr, curr_input_index, name);
4771 let curr_inputs_index =
4772 self.builder
4773 .build_int_add(inputs_start_index, curr_input_index, name)?;
4774 let inputs_ptr = Self::get_ptr_to_index(
4775 &self.builder,
4776 self.real_type,
4777 self.get_param("inputs"),
4778 curr_inputs_index,
4779 name,
4780 );
4781 if is_get {
4782 let input_value = self
4783 .build_load(self.real_type, input_ptr, name)?
4784 .into_float_value();
4785 self.builder.build_store(inputs_ptr, input_value)?;
4786 } else {
4787 let input_value = self
4788 .build_load(self.real_type, inputs_ptr, name)?
4789 .into_float_value();
4790 self.builder.build_store(input_ptr, input_value)?;
4791 }
4792
4793 let one = self.int_type.const_int(1, false);
4795 let next_index = self.builder.build_int_add(curr_input_index, one, name)?;
4796 index.add_incoming(&[(&next_index, input_block)]);
4797
4798 let loop_while =
4800 self.builder
4801 .build_int_compare(IntPredicate::ULT, next_index, end_index, name)?;
4802 let post_block = self.context.append_basic_block(function, name);
4803 self.builder
4804 .build_conditional_branch(loop_while, input_block, post_block)?;
4805 self.builder.position_at_end(post_block);
4806 }
4807 self.builder.build_return(None)?;
4808
4809 if function.verify(true) {
4810 Ok(function)
4811 } else {
4812 function.print_to_stderr();
4813 unsafe {
4814 function.delete();
4815 }
4816 Err(anyhow!("Invalid generated function."))
4817 }
4818 }
4819
4820 pub fn compile_set_id(&mut self, model: &DiscreteModel) -> Result<FunctionValue<'ctx>> {
4821 self.clear();
4822 let fn_arg_names = &["id"];
4823 let function = self.add_function(
4824 "set_id",
4825 fn_arg_names,
4826 &[self.real_ptr_type.into()],
4827 None,
4828 false,
4829 );
4830 let mut block = self.start_function(function, None);
4831
4832 for (i, arg) in function.get_param_iter().enumerate() {
4833 let name = fn_arg_names[i];
4834 let alloca = self.function_arg_alloca(name, arg);
4835 self.insert_param(name, alloca);
4836 }
4837
4838 let mut id_index = 0usize;
4839 for (blk, is_algebraic) in zip(model.state().elmts(), model.is_algebraic()) {
4840 let name = blk.name().unwrap_or("unknown");
4841 let id_start_index = self.int_type.const_int(id_index as u64, false);
4843 let blk_start_index = self.int_type.const_int(0, false);
4844 let blk_end_index = self
4845 .int_type
4846 .const_int(blk.nnz().try_into().unwrap(), false);
4847
4848 let blk_block = self.context.append_basic_block(function, name);
4849 self.builder.build_unconditional_branch(blk_block)?;
4850 self.builder.position_at_end(blk_block);
4851 let index = self.builder.build_phi(self.int_type, "i")?;
4852 index.add_incoming(&[(&blk_start_index, block)]);
4853
4854 let curr_blk_index = index.as_basic_value().into_int_value();
4856 let curr_id_index = self
4857 .builder
4858 .build_int_add(id_start_index, curr_blk_index, name)?;
4859 let id_ptr = Self::get_ptr_to_index(
4860 &self.builder,
4861 self.real_type,
4862 self.get_param("id"),
4863 curr_id_index,
4864 name,
4865 );
4866 let is_algebraic_float = if *is_algebraic { 0.0 } else { 1.0 };
4867 let is_algebraic_value = self.real_type.const_float(is_algebraic_float);
4868 self.builder.build_store(id_ptr, is_algebraic_value)?;
4869
4870 let one = self.int_type.const_int(1, false);
4872 let next_index = self.builder.build_int_add(curr_blk_index, one, name)?;
4873 index.add_incoming(&[(&next_index, blk_block)]);
4874
4875 let loop_while = self.builder.build_int_compare(
4877 IntPredicate::ULT,
4878 next_index,
4879 blk_end_index,
4880 name,
4881 )?;
4882 let post_block = self.context.append_basic_block(function, name);
4883 self.builder
4884 .build_conditional_branch(loop_while, blk_block, post_block)?;
4885 self.builder.position_at_end(post_block);
4886
4887 block = post_block;
4889 id_index += blk.nnz();
4890 }
4891 self.builder.build_return(None)?;
4892
4893 if function.verify(true) {
4894 Ok(function)
4895 } else {
4896 function.print_to_stderr();
4897 unsafe {
4898 function.delete();
4899 }
4900 Err(anyhow!("Invalid generated function."))
4901 }
4902 }
4903
4904 pub fn module(&self) -> &Module<'ctx> {
4905 &self.module
4906 }
4907}