1use super::maps::MapManager;
7use crate::script::{VarType, VariableContext};
8use ghostscope_dwarf::DwarfAnalyzer;
9use inkwell::builder::Builder;
10use inkwell::context::Context;
11use inkwell::debug_info::DebugInfoBuilder;
12use inkwell::module::Module;
13use inkwell::targets::{Target, TargetTriple};
14use inkwell::values::{FunctionValue, IntValue, PointerValue};
15use inkwell::AddressSpace;
16use inkwell::OptimizationLevel;
17use std::collections::HashMap;
18use thiserror::Error;
19use tracing::info;
20
21#[derive(Debug, Clone)]
23pub struct CompileTimeContext {
24 pub pc_address: u64,
25 pub module_path: String,
26}
27
28#[derive(Error, Debug)]
29pub enum CodeGenError {
30 #[error("LLVM compilation error: {0}")]
31 LLVMError(String),
32 #[error("Unsupported evaluation result: {0}")]
33 UnsupportedEvaluation(String),
34 #[error("Register mapping error: {0}")]
35 RegisterMappingError(String),
36 #[error("Memory access error: {0}")]
37 MemoryAccessError(String),
38 #[error("Builder error: {0}")]
39 Builder(String),
40
41 #[error("Variable not found: {0}")]
43 VariableNotFound(String),
44 #[error("Variable not in scope: {0}")]
45 VariableNotInScope(String),
46 #[error("Type error: {0}")]
47 TypeError(String),
48 #[error("Not implemented: {0}")]
49 NotImplemented(String),
50 #[error("DWARF expression error: {0}")]
51 DwarfError(String),
52 #[error("Type size not available for variable: {0}")]
53 TypeSizeNotAvailable(String),
54}
55
56pub type Result<T> = std::result::Result<T, CodeGenError>;
57
58pub struct EbpfContext<'ctx, 'dw> {
60 pub context: &'ctx Context,
61 pub module: Module<'ctx>,
62 pub builder: Builder<'ctx>,
63
64 pub trace_printk_fn: FunctionValue<'ctx>,
66
67 pub map_manager: MapManager<'ctx>,
69
70 pub di_builder: DebugInfoBuilder<'ctx>,
72 pub compile_unit: inkwell::debug_info::DICompileUnit<'ctx>,
73
74 pub register_cache: HashMap<u16, IntValue<'ctx>>,
76
77 pub variables: HashMap<String, PointerValue<'ctx>>, pub var_types: HashMap<String, VarType>, pub optimized_out_vars: HashMap<String, bool>, pub var_pc_addresses: HashMap<String, u64>, pub variable_context: Option<VariableContext>, pub(super) process_analyzer: Option<&'dw DwarfAnalyzer>, pub current_trace_id: Option<u32>, pub current_compile_time_context: Option<CompileTimeContext>, pub trace_context: ghostscope_protocol::TraceContext, pub current_resolved_var_module_path: Option<String>,
90
91 pub pm_key_alloca: Option<inkwell::values::PointerValue<'ctx>>,
94 pub event_offset_alloca: Option<inkwell::values::PointerValue<'ctx>>,
96 pub compile_time_event_bytes_upper_bound: usize,
100 pub offsets_found_flag: Option<inkwell::values::PointerValue<'ctx>>,
102
103 pub compile_options: crate::CompileOptions,
105
106 pub condition_context_active: bool,
108
109 pub alias_vars: HashMap<String, crate::script::Expr>,
113
114 pub string_vars: HashMap<String, Vec<u8>>,
118
119 pub scope_stack: Vec<std::collections::HashSet<String>>,
122}
123
124pub type NewCodeGen<'ctx, 'dw> = EbpfContext<'ctx, 'dw>;
126
127impl<'ctx, 'dw> EbpfContext<'ctx, 'dw> {
128 pub fn new(
130 context: &'ctx Context,
131 module_name: &str,
132 trace_id: Option<u32>,
133 compile_options: &crate::CompileOptions,
134 ) -> Result<Self> {
135 let module = context.create_module(module_name);
136 let builder = context.create_builder();
137
138 Target::initialize_bpf(&Default::default());
140
141 let triple = TargetTriple::create("bpf-pc-linux");
143
144 let target = Target::from_triple(&triple).map_err(|e| {
146 CodeGenError::LLVMError(format!("Failed to get target from triple: {e}"))
147 })?;
148 let target_machine = target
149 .create_target_machine(
150 &triple,
151 "generic",
152 "+alu32",
153 OptimizationLevel::Default,
154 inkwell::targets::RelocMode::PIC,
155 inkwell::targets::CodeModel::Small,
156 )
157 .ok_or_else(|| {
158 CodeGenError::LLVMError("Failed to create target machine".to_string())
159 })?;
160
161 let data_layout = target_machine.get_target_data().get_data_layout();
163 module.set_data_layout(&data_layout);
164 module.set_triple(&triple);
165
166 let (di_builder, compile_unit) = module.create_debug_info_builder(
168 true, inkwell::debug_info::DWARFSourceLanguage::C, "ghostscope_generated.c", ".", "ghostscope-compiler", false, "", 1, "", inkwell::debug_info::DWARFEmissionKind::Full, 0, false, false, "", "", );
184
185 let map_manager = MapManager::new(context);
186
187 let trace_printk_fn = Self::declare_trace_printk(context, &module);
189
190 Ok(Self {
191 context,
192 module,
193 builder,
194 trace_printk_fn,
195 map_manager,
196 di_builder,
197 compile_unit,
198 register_cache: HashMap::new(),
199
200 variables: HashMap::new(),
202 var_types: HashMap::new(),
203 optimized_out_vars: HashMap::new(),
204 var_pc_addresses: HashMap::new(),
205 variable_context: None,
206 process_analyzer: None,
207 current_trace_id: trace_id,
208 current_compile_time_context: None,
209
210 trace_context: ghostscope_protocol::TraceContext::new(),
212 current_resolved_var_module_path: None,
213 pm_key_alloca: None,
214 event_offset_alloca: None,
215 compile_time_event_bytes_upper_bound: 0,
216 offsets_found_flag: None,
217 compile_options: compile_options.clone(),
218
219 condition_context_active: false,
221
222 alias_vars: HashMap::new(),
224 string_vars: HashMap::new(),
226
227 scope_stack: Vec::new(),
229 })
230 }
231
232 pub fn enter_scope(&mut self) {
234 self.scope_stack.push(std::collections::HashSet::new());
235 }
236
237 pub fn exit_scope(&mut self) {
239 if let Some(names) = self.scope_stack.pop() {
240 for name in names {
241 self.variables.remove(&name);
242 self.var_types.remove(&name);
243 self.alias_vars.remove(&name);
244 self.string_vars.remove(&name);
245 self.optimized_out_vars.remove(&name);
246 self.var_pc_addresses.remove(&name);
247 }
248 }
249 }
250
251 pub fn is_name_in_any_scope(&self, name: &str) -> bool {
253 self.scope_stack.iter().any(|s| s.contains(name))
254 }
255
256 pub fn is_name_in_current_scope(&self, name: &str) -> bool {
258 match self.scope_stack.last() {
259 Some(top) => top.contains(name),
260 None => false,
261 }
262 }
263
264 pub fn declare_name_in_current_scope(&mut self, name: &str) -> Result<()> {
266 if self.scope_stack.is_empty() {
267 self.enter_scope();
269 }
270 if self.is_name_in_current_scope(name) {
271 return Err(CodeGenError::TypeError(format!(
272 "Redeclaration in the same scope is not allowed: '{name}'"
273 )));
274 }
275 if self.is_name_in_any_scope(name) {
276 return Err(CodeGenError::TypeError(format!(
277 "Shadowing is not allowed for immutable variables: '{name}'"
278 )));
279 }
280 if let Some(top) = self.scope_stack.last_mut() {
281 top.insert(name.to_string());
282 }
283 Ok(())
284 }
285
286 pub fn new_with_process_analyzer(
288 context: &'ctx Context,
289 module_name: &str,
290 process_analyzer: Option<&'dw DwarfAnalyzer>,
291 trace_id: Option<u32>,
292 compile_options: &crate::CompileOptions,
293 ) -> Result<Self> {
294 let mut codegen = Self::new(context, module_name, trace_id, compile_options)?;
295 codegen.process_analyzer = process_analyzer;
296 Ok(codegen)
297 }
298
299 pub fn set_compile_time_context(&mut self, pc_address: u64, module_path: String) {
301 self.current_compile_time_context = Some(CompileTimeContext {
302 pc_address,
303 module_path,
304 });
305 }
306
307 pub fn get_compile_time_context(&self) -> Result<&CompileTimeContext> {
309 self.current_compile_time_context
310 .as_ref()
311 .ok_or_else(|| CodeGenError::DwarfError("No compile-time context set".to_string()))
312 }
313
314 pub fn take_module_hint(&mut self) -> Option<String> {
316 self.current_resolved_var_module_path.take()
317 }
318
319 fn declare_trace_printk(context: &'ctx Context, module: &Module<'ctx>) -> FunctionValue<'ctx> {
321 let i32_type = context.i32_type();
322 let ptr_type = context.ptr_type(AddressSpace::default());
323 let i64_type = context.i64_type();
324
325 let fn_type = i32_type.fn_type(&[ptr_type.into(), i64_type.into()], true);
327
328 module.add_function("bpf_trace_printk", fn_type, None)
329 }
330
331 pub fn create_basic_ebpf_function(&mut self, function_name: &str) -> Result<()> {
333 let i32_type = self.context.i32_type();
334 let ptr_type = self.context.ptr_type(AddressSpace::default());
335
336 let fn_type = i32_type.fn_type(&[ptr_type.into()], false);
338
339 let function = self.module.add_function(function_name, fn_type, None);
340
341 function.add_attribute(
343 inkwell::attributes::AttributeLoc::Function,
344 self.context.create_string_attribute("section", "uprobe"),
345 );
346
347 let basic_block = self.context.append_basic_block(function, "entry");
349 self.builder.position_at_end(basic_block);
350
351 info!("Created eBPF function: {}", function_name);
352 Ok(())
353 }
354
355 #[cfg(test)]
357 pub fn __test_ensure_proc_offsets_map(&mut self) -> Result<()> {
358 self.map_manager
359 .create_proc_module_offsets_map(
360 &self.module,
361 &self.di_builder,
362 &self.compile_unit,
363 "proc_module_offsets",
364 self.compile_options.proc_module_offsets_max_entries,
365 )
366 .map_err(|e| {
367 CodeGenError::LLVMError(format!(
368 "Failed to create proc_module_offsets map in test: {e}"
369 ))
370 })?;
371 self.map_manager
372 .create_pid_aliases_map(
373 &self.module,
374 &self.di_builder,
375 &self.compile_unit,
376 "pid_aliases",
377 self.compile_options.proc_module_offsets_max_entries,
378 )
379 .map_err(|e| {
380 CodeGenError::LLVMError(format!("Failed to create pid_aliases map in test: {e}"))
381 })
382 }
383
384 #[cfg(test)]
386 pub fn __test_alloc_pm_key(&mut self) -> Result<()> {
387 let i32_type = self.context.i32_type();
388 let key_arr_ty = i32_type.array_type(4);
389 let key_alloca = self
390 .builder
391 .build_alloca(key_arr_ty, "pm_key")
392 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
393 self.pm_key_alloca = Some(key_alloca);
394 Ok(())
395 }
396
397 pub fn get_module(&self) -> &Module<'ctx> {
399 &self.module
400 }
401
402 pub fn get_trace_context(&self) -> ghostscope_protocol::TraceContext {
404 self.trace_context.clone()
405 }
406
407 pub fn get_pt_regs_parameter(&self) -> Result<PointerValue<'ctx>> {
409 let current_function = self
410 .builder
411 .get_insert_block()
412 .ok_or_else(|| CodeGenError::Builder("No current basic block".to_string()))?
413 .get_parent()
414 .ok_or_else(|| CodeGenError::Builder("No parent function".to_string()))?;
415
416 let pt_regs_param = current_function
417 .get_first_param()
418 .ok_or_else(|| CodeGenError::Builder("Function has no parameters".to_string()))?
419 .into_pointer_value();
420
421 Ok(pt_regs_param)
422 }
423
424 pub fn compile_program(
426 &mut self,
427 _program: &crate::script::Program,
428 function_name: &str,
429 trace_statements: &[crate::script::Statement],
430 target_pid: Option<u32>,
431 compile_time_pc: Option<u64>,
432 module_path: Option<&str>,
433 ) -> Result<(FunctionValue<'ctx>, ghostscope_protocol::TraceContext)> {
434 info!(
435 "Starting program compilation with function: {}",
436 function_name
437 );
438
439 self.current_compile_time_context =
441 if let (Some(pc), Some(path)) = (compile_time_pc, module_path) {
442 Some(CompileTimeContext {
443 pc_address: pc,
444 module_path: path.to_string(),
445 })
446 } else {
447 None
448 };
449
450 match self.compile_options.event_map_type {
453 crate::EventMapType::RingBuf => {
454 self.map_manager
455 .create_ringbuf_map(
456 &self.module,
457 &self.di_builder,
458 &self.compile_unit,
459 "ringbuf",
460 self.compile_options.ringbuf_size,
461 )
462 .map_err(|e| {
463 CodeGenError::LLVMError(format!("Failed to create ringbuf map: {e}"))
464 })?;
465 }
466 crate::EventMapType::PerfEventArray => {
467 self.map_manager
468 .create_perf_event_array_map(
469 &self.module,
470 &self.di_builder,
471 &self.compile_unit,
472 "events",
473 )
474 .map_err(|e| {
475 CodeGenError::LLVMError(format!(
476 "Failed to create perf event array map: {e}"
477 ))
478 })?;
479 }
480 }
481
482 self.map_manager
486 .create_percpu_array_map(
487 &self.module,
488 &self.di_builder,
489 &self.compile_unit,
490 "event_accum_buffer",
491 1,
492 self.compile_options.max_trace_event_size as u64,
493 )
494 .map_err(|e| {
495 CodeGenError::LLVMError(format!("Failed to create event_accum_buffer: {e}"))
496 })?;
497
498 self.map_manager
500 .create_proc_module_offsets_map(
501 &self.module,
502 &self.di_builder,
503 &self.compile_unit,
504 "proc_module_offsets",
505 self.compile_options.proc_module_offsets_max_entries,
506 )
507 .map_err(|e| {
508 CodeGenError::LLVMError(format!("Failed to create proc_module_offsets map: {e}"))
509 })?;
510
511 self.map_manager
512 .create_pid_aliases_map(
513 &self.module,
514 &self.di_builder,
515 &self.compile_unit,
516 "pid_aliases",
517 self.compile_options.proc_module_offsets_max_entries,
518 )
519 .map_err(|e| {
520 CodeGenError::LLVMError(format!("Failed to create pid_aliases map: {e}"))
521 })?;
522
523 let main_function = self.create_main_function(function_name)?;
528
529 let pid_filter_spec = self
533 .compile_options
534 .pid_filter_spec
535 .or_else(|| target_pid.map(|pid| crate::PidFilterSpec::HostTgid { filter_pid: pid }));
536 if let Some(spec) = pid_filter_spec {
537 self.add_pid_filter(spec)?;
538 }
539
540 let program = crate::script::ast::Program {
542 statements: trace_statements.to_vec(),
543 };
544
545 let variable_types = std::collections::HashMap::new(); let trace_context =
550 self.compile_program_with_staged_transmission(&program, variable_types)?;
551 info!(
552 "Generated TraceContext with {} strings",
553 trace_context.string_count()
554 );
555
556 let i32_type = self.context.i32_type();
558 let return_value = i32_type.const_int(0, false);
559 self.builder
560 .build_return(Some(&return_value))
561 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
562
563 info!(
564 "Successfully compiled program with function: {} and TraceContext",
565 function_name
566 );
567 Ok((main_function, trace_context))
568 }
569
570 fn create_main_function(&mut self, function_name: &str) -> Result<FunctionValue<'ctx>> {
572 let i32_type = self.context.i32_type();
573 let ptr_type = self.context.ptr_type(AddressSpace::default());
574
575 let fn_type = i32_type.fn_type(&[ptr_type.into()], false);
577 let function = self.module.add_function(function_name, fn_type, None);
578
579 function.set_section(Some("uprobe"));
581
582 let basic_block = self.context.append_basic_block(function, "entry");
584 self.builder.position_at_end(basic_block);
585
586 let key_arr_ty = i32_type.array_type(4);
591 let key_alloca = self
592 .builder
593 .build_alloca(key_arr_ty, "pm_key")
594 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
595 self.pm_key_alloca = Some(key_alloca);
596
597 let event_off_alloca = self
599 .builder
600 .build_alloca(i32_type, "event_offset")
601 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
602 self.builder
603 .build_store(event_off_alloca, i32_type.const_zero())
604 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
605 self.event_offset_alloca = Some(event_off_alloca);
606
607 info!("Created main function: {}", function_name);
608 Ok(function)
609 }
610
611 fn add_pid_filter(&mut self, spec: crate::PidFilterSpec) -> Result<()> {
614 match spec {
615 crate::PidFilterSpec::HostTgid { filter_pid } => self.add_host_pid_filter(filter_pid),
616 crate::PidFilterSpec::NamespaceTgid { filter_pid, pid_ns } => {
617 let (pid_ns_dev, pid_ns_inode) = pid_ns.helper_dev_inode().ok_or_else(|| {
618 CodeGenError::LLVMError(
619 "Namespace TGID filter requires pid namespace device id".to_string(),
620 )
621 })?;
622 self.add_namespace_pid_filter(filter_pid, pid_ns_dev, pid_ns_inode)
623 }
624 }
625 }
626
627 fn add_host_pid_filter(&mut self, filter_pid: u32) -> Result<()> {
628 info!("Adding host TGID filter for filter PID: {}", filter_pid);
629
630 let current_fn = self
632 .builder
633 .get_insert_block()
634 .unwrap()
635 .get_parent()
636 .unwrap();
637
638 let continue_block = self
640 .context
641 .append_basic_block(current_fn, "continue_execution");
642 let early_return_block = self
643 .context
644 .append_basic_block(current_fn, "pid_mismatch_return");
645
646 let pid_tgid_value = self.get_current_pid_tgid()?;
648
649 let shift_amount = self.context.i64_type().const_int(32, false);
651 let current_tgid = self
652 .builder
653 .build_right_shift(pid_tgid_value, shift_amount, false, "current_tgid")
654 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
655
656 let target_pid_value = self.context.i64_type().const_int(filter_pid as u64, false);
658 let pid_matches = self
659 .builder
660 .build_int_compare(
661 inkwell::IntPredicate::EQ,
662 current_tgid,
663 target_pid_value,
664 "pid_matches",
665 )
666 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
667
668 self.builder
670 .build_conditional_branch(pid_matches, continue_block, early_return_block)
671 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
672
673 self.builder.position_at_end(early_return_block);
675 self.builder
676 .build_return(Some(&self.context.i32_type().const_int(0, false)))
677 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
678
679 self.builder.position_at_end(continue_block);
681
682 info!(
683 "Host TGID filter added successfully for filter PID: {}",
684 filter_pid
685 );
686 Ok(())
687 }
688
689 fn add_namespace_pid_filter(
690 &mut self,
691 filter_pid: u32,
692 pid_ns_dev: u64,
693 pid_ns_inode: u64,
694 ) -> Result<()> {
695 const BPF_FUNC_GET_NS_CURRENT_PID_TGID: u64 = 120;
696 const BPF_PIDNS_INFO_SIZE: u64 = 8; info!(
699 "Adding namespace TGID filter: filter_pid={} ns_dev={} ns_inode={}",
700 filter_pid, pid_ns_dev, pid_ns_inode
701 );
702
703 let current_fn = self
704 .builder
705 .get_insert_block()
706 .ok_or_else(|| CodeGenError::Builder("No current insert block".to_string()))?
707 .get_parent()
708 .ok_or_else(|| CodeGenError::Builder("No parent function".to_string()))?;
709
710 let helper_ok_block = self
711 .context
712 .append_basic_block(current_fn, "pidns_helper_ok");
713 let continue_block = self
714 .context
715 .append_basic_block(current_fn, "continue_execution");
716 let early_return_block = self
717 .context
718 .append_basic_block(current_fn, "pid_mismatch_return");
719
720 let i32_type = self.context.i32_type();
721 let i64_type = self.context.i64_type();
722 let ptr_type = self.context.ptr_type(AddressSpace::default());
723
724 let pidns_info_ty = i32_type.array_type(2);
726 let pidns_info_alloca = self
727 .builder
728 .build_alloca(pidns_info_ty, "pidns_info")
729 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
730 self.builder
731 .build_store(pidns_info_alloca, pidns_info_ty.const_zero())
732 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
733
734 let pidns_info_ptr = self
735 .builder
736 .build_bit_cast(pidns_info_alloca, ptr_type, "pidns_info_ptr")
737 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
738
739 let helper_args = [
740 i64_type.const_int(pid_ns_dev, false).into(),
741 i64_type.const_int(pid_ns_inode, false).into(),
742 pidns_info_ptr,
743 i64_type.const_int(BPF_PIDNS_INFO_SIZE, false).into(),
744 ];
745 let helper_ret = self.create_bpf_helper_call(
746 BPF_FUNC_GET_NS_CURRENT_PID_TGID,
747 &helper_args,
748 i64_type.into(),
749 "ns_pid_tgid_ret",
750 )?;
751 let helper_ret = match helper_ret {
752 inkwell::values::BasicValueEnum::IntValue(v) => v,
753 _ => {
754 return Err(CodeGenError::LLVMError(
755 "bpf_get_ns_current_pid_tgid did not return integer".to_string(),
756 ));
757 }
758 };
759
760 let helper_ok = self
761 .builder
762 .build_int_compare(
763 inkwell::IntPredicate::EQ,
764 helper_ret,
765 i64_type.const_zero(),
766 "pidns_helper_ok",
767 )
768 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
769 self.builder
770 .build_conditional_branch(helper_ok, helper_ok_block, early_return_block)
771 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
772
773 self.builder.position_at_end(helper_ok_block);
774 let tgid_ptr = unsafe {
775 self.builder.build_gep(
776 pidns_info_ty,
777 pidns_info_alloca,
778 &[i32_type.const_zero(), i32_type.const_int(1, false)],
779 "pidns_tgid_ptr",
780 )
781 }
782 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
783 let ns_tgid = self
784 .builder
785 .build_load(i32_type, tgid_ptr, "ns_tgid")
786 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
787 .into_int_value();
788 let ns_tgid_i64 = self
789 .builder
790 .build_int_z_extend(ns_tgid, i64_type, "ns_tgid_i64")
791 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
792 let target_pid_value = i64_type.const_int(filter_pid as u64, false);
793 let pid_matches = self
794 .builder
795 .build_int_compare(
796 inkwell::IntPredicate::EQ,
797 ns_tgid_i64,
798 target_pid_value,
799 "pid_matches_ns",
800 )
801 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
802 self.builder
803 .build_conditional_branch(pid_matches, continue_block, early_return_block)
804 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
805
806 self.builder.position_at_end(early_return_block);
807 self.builder
808 .build_return(Some(&self.context.i32_type().const_int(0, false)))
809 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
810
811 self.builder.position_at_end(continue_block);
812 info!(
813 "Namespace TGID filter added successfully for filter PID: {}",
814 filter_pid
815 );
816 Ok(())
817 }
818
819 pub fn get_or_create_flag_global(&mut self, name: &str) -> PointerValue<'ctx> {
821 if let Some(g) = self.module.get_global(name) {
822 return g.as_pointer_value();
823 }
824 let i8_type = self.context.i8_type();
825 let global = self
826 .module
827 .add_global(i8_type, Some(AddressSpace::default()), name);
828 global.set_initializer(&i8_type.const_zero());
829 global.as_pointer_value()
830 }
831
832 pub fn store_flag_value(&mut self, name: &str, value: u8) -> Result<()> {
834 let ptr = self.get_or_create_flag_global(name);
835 self.builder
836 .build_store(ptr, self.context.i8_type().const_int(value as u64, false))
837 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store flag {name}: {e}")))?;
838 Ok(())
839 }
840
841 pub fn mark_any_success(&mut self) -> Result<()> {
843 self.store_flag_value("_gs_any_success", 1)
844 }
845
846 pub fn mark_any_fail(&mut self) -> Result<()> {
848 self.store_flag_value("_gs_any_fail", 1)
849 }
850
851 pub fn get_or_create_offsets_found_flag(&mut self) -> inkwell::values::PointerValue<'ctx> {
854 if let Some(ptr) = self.offsets_found_flag {
855 return ptr;
856 }
857 let i8_type = self.context.i8_type();
858 let global =
860 self.module
861 .add_global(i8_type, Some(AddressSpace::default()), "_gs_offsets_found");
862 global.set_initializer(&i8_type.const_int(1, false));
863 let ptr = global.as_pointer_value();
864 self.offsets_found_flag = Some(ptr);
865 ptr
866 }
867
868 pub fn store_offsets_found_flag(
870 &mut self,
871 flag: inkwell::values::IntValue<'ctx>,
872 ) -> Result<()> {
873 let ptr = self.get_or_create_offsets_found_flag();
874 let i8_type = self.context.i8_type();
875 let flag_i8 = self
876 .builder
877 .build_int_z_extend(flag, i8_type, "offset_flag_i8")
878 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
879 self.builder
880 .build_store(ptr, flag_i8)
881 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
882 Ok(())
883 }
884
885 pub fn store_offsets_found_const(&mut self, value: bool) -> Result<()> {
887 let ptr = self.get_or_create_offsets_found_flag();
888 let i8_type = self.context.i8_type();
889 self.builder
890 .build_store(ptr, i8_type.const_int(value as u64, false))
891 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
892 Ok(())
893 }
894
895 pub fn load_offsets_found_flag(&mut self) -> Result<inkwell::values::IntValue<'ctx>> {
897 let ptr = self.get_or_create_offsets_found_flag();
898 let i8_type = self.context.i8_type();
899 let raw = self
900 .builder
901 .build_load(i8_type, ptr, "offsets_found_raw")
902 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
903 .into_int_value();
904 let is_non_zero = self
905 .builder
906 .build_int_compare(
907 inkwell::IntPredicate::NE,
908 raw,
909 i8_type.const_zero(),
910 "offsets_found_bool",
911 )
912 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
913 Ok(is_non_zero)
914 }
915
916 pub fn get_or_create_cond_error_global(&mut self) -> PointerValue<'ctx> {
918 if let Some(g) = self.module.get_global("_gs_cond_error") {
919 return g.as_pointer_value();
920 }
921 let i8_type = self.context.i8_type();
922 let global =
923 self.module
924 .add_global(i8_type, Some(AddressSpace::default()), "_gs_cond_error");
925 global.set_initializer(&i8_type.const_zero());
926 global.as_pointer_value()
927 }
928
929 pub fn reset_condition_error(&mut self) -> Result<()> {
931 let ptr = self.get_or_create_cond_error_global();
932 self.builder
933 .build_store(ptr, self.context.i8_type().const_zero())
934 .map_err(|e| CodeGenError::LLVMError(format!("Failed to reset _gs_cond_error: {e}")))?;
935 let aptr = self.get_or_create_cond_error_addr_global();
937 self.builder
938 .build_store(aptr, self.context.i64_type().const_zero())
939 .map_err(|e| {
940 CodeGenError::LLVMError(format!("Failed to reset _gs_cond_error_addr: {e}"))
941 })?;
942 let fptr = self.get_or_create_cond_error_flags_global();
944 self.builder
945 .build_store(fptr, self.context.i8_type().const_zero())
946 .map_err(|e| {
947 CodeGenError::LLVMError(format!("Failed to reset _gs_cond_error_flags: {e}"))
948 })?;
949 Ok(())
950 }
951
952 pub fn get_or_create_cond_error_addr_global(&mut self) -> PointerValue<'ctx> {
954 if let Some(g) = self.module.get_global("_gs_cond_error_addr") {
955 return g.as_pointer_value();
956 }
957 let i64_type = self.context.i64_type();
958 let global = self.module.add_global(
959 i64_type,
960 Some(AddressSpace::default()),
961 "_gs_cond_error_addr",
962 );
963 global.set_initializer(&i64_type.const_zero());
964 global.as_pointer_value()
965 }
966
967 pub fn set_condition_error_if_unset(&mut self, code: u8) -> Result<()> {
969 if !self.condition_context_active {
970 return Ok(());
971 }
972 let ptr = self.get_or_create_cond_error_global();
973 let cur = self
974 .builder
975 .build_load(self.context.i8_type(), ptr, "cond_err_cur")
976 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
977 .into_int_value();
978 let is_zero = self
979 .builder
980 .build_int_compare(
981 inkwell::IntPredicate::EQ,
982 cur,
983 self.context.i8_type().const_zero(),
984 "cond_err_is_zero",
985 )
986 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
987 let newv_bv: inkwell::values::BasicValueEnum =
988 self.context.i8_type().const_int(code as u64, false).into();
989 let sel = self
990 .builder
991 .build_select::<inkwell::values::BasicValueEnum, _>(
992 is_zero,
993 newv_bv,
994 cur.into(),
995 "cond_err_new",
996 )
997 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
998 self.builder
999 .build_store(ptr, sel)
1000 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1001 Ok(())
1002 }
1003
1004 pub fn build_condition_error_predicate(&mut self) -> Result<inkwell::values::IntValue<'ctx>> {
1006 let ptr = self.get_or_create_cond_error_global();
1007 let cur = self
1008 .builder
1009 .build_load(self.context.i8_type(), ptr, "cond_err_cur")
1010 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
1011 .into_int_value();
1012 self.builder
1013 .build_int_compare(
1014 inkwell::IntPredicate::NE,
1015 cur,
1016 self.context.i8_type().const_zero(),
1017 "cond_err_nonzero",
1018 )
1019 .map_err(|e| CodeGenError::LLVMError(e.to_string()))
1020 }
1021
1022 pub fn get_or_create_cond_error_flags_global(&mut self) -> PointerValue<'ctx> {
1024 if let Some(g) = self.module.get_global("_gs_cond_error_flags") {
1025 return g.as_pointer_value();
1026 }
1027 let i8_type = self.context.i8_type();
1028 let global = self.module.add_global(
1029 i8_type,
1030 Some(AddressSpace::default()),
1031 "_gs_cond_error_flags",
1032 );
1033 global.set_initializer(&i8_type.const_zero());
1034 global.as_pointer_value()
1035 }
1036
1037 pub fn or_condition_error_flags(&mut self, flags: IntValue<'ctx>) -> Result<()> {
1039 if !self.condition_context_active {
1040 return Ok(());
1041 }
1042 let ptr = self.get_or_create_cond_error_flags_global();
1043 let cur = self
1044 .builder
1045 .build_load(self.context.i8_type(), ptr, "cond_err_flags_cur")
1046 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
1047 .into_int_value();
1048 let newv = self
1049 .builder
1050 .build_or(cur, flags, "cond_err_flags_or")
1051 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1052 self.builder
1053 .build_store(ptr, newv)
1054 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1055 Ok(())
1056 }
1057
1058 pub fn set_condition_error_addr_if_unset(&mut self, addr: IntValue<'ctx>) -> Result<()> {
1060 if !self.condition_context_active {
1061 return Ok(());
1062 }
1063 let ptr = self.get_or_create_cond_error_addr_global();
1064 let cur = self
1065 .builder
1066 .build_load(self.context.i64_type(), ptr, "cond_err_addr_cur")
1067 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
1068 .into_int_value();
1069 let is_zero = self
1070 .builder
1071 .build_int_compare(
1072 inkwell::IntPredicate::EQ,
1073 cur,
1074 self.context.i64_type().const_zero(),
1075 "cond_err_addr_is_zero",
1076 )
1077 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1078 let sel = self
1079 .builder
1080 .build_select::<IntValue<'ctx>, _>(is_zero, addr, cur, "cond_err_addr_new")
1081 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1082 self.builder
1083 .build_store(ptr, sel)
1084 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1085 Ok(())
1086 }
1087}