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> {
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 process_analyzer: Option<*mut 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>>, pub event_offset_alloca: Option<inkwell::values::PointerValue<'ctx>>,
95 pub offsets_found_flag: Option<inkwell::values::PointerValue<'ctx>>,
97
98 pub compile_options: crate::CompileOptions,
100
101 pub condition_context_active: bool,
103
104 pub alias_vars: HashMap<String, crate::script::Expr>,
108
109 pub string_vars: HashMap<String, Vec<u8>>,
113
114 pub scope_stack: Vec<std::collections::HashSet<String>>,
117}
118
119pub type NewCodeGen<'ctx> = EbpfContext<'ctx>;
121
122impl<'ctx> EbpfContext<'ctx> {
123 pub fn new(
125 context: &'ctx Context,
126 module_name: &str,
127 trace_id: Option<u32>,
128 compile_options: &crate::CompileOptions,
129 ) -> Result<Self> {
130 let module = context.create_module(module_name);
131 let builder = context.create_builder();
132
133 Target::initialize_bpf(&Default::default());
135
136 let triple = TargetTriple::create("bpf-pc-linux");
138
139 let target = Target::from_triple(&triple).map_err(|e| {
141 CodeGenError::LLVMError(format!("Failed to get target from triple: {e}"))
142 })?;
143 let target_machine = target
144 .create_target_machine(
145 &triple,
146 "generic",
147 "+alu32",
148 OptimizationLevel::Default,
149 inkwell::targets::RelocMode::PIC,
150 inkwell::targets::CodeModel::Small,
151 )
152 .ok_or_else(|| {
153 CodeGenError::LLVMError("Failed to create target machine".to_string())
154 })?;
155
156 let data_layout = target_machine.get_target_data().get_data_layout();
158 module.set_data_layout(&data_layout);
159 module.set_triple(&triple);
160
161 let (di_builder, compile_unit) = module.create_debug_info_builder(
163 true, inkwell::debug_info::DWARFSourceLanguage::C, "ghostscope_generated.c", ".", "ghostscope-compiler", false, "", 1, "", inkwell::debug_info::DWARFEmissionKind::Full, 0, false, false, "", "", );
179
180 let map_manager = MapManager::new(context);
181
182 let trace_printk_fn = Self::declare_trace_printk(context, &module);
184
185 Ok(Self {
186 context,
187 module,
188 builder,
189 trace_printk_fn,
190 map_manager,
191 di_builder,
192 compile_unit,
193 register_cache: HashMap::new(),
194
195 variables: HashMap::new(),
197 var_types: HashMap::new(),
198 optimized_out_vars: HashMap::new(),
199 var_pc_addresses: HashMap::new(),
200 variable_context: None,
201 process_analyzer: None,
202 current_trace_id: trace_id,
203 current_compile_time_context: None,
204
205 trace_context: ghostscope_protocol::TraceContext::new(),
207 current_resolved_var_module_path: None,
208 pm_key_alloca: None,
209 event_offset_alloca: None,
210 offsets_found_flag: None,
211 compile_options: compile_options.clone(),
212
213 condition_context_active: false,
215
216 alias_vars: HashMap::new(),
218 string_vars: HashMap::new(),
220
221 scope_stack: Vec::new(),
223 })
224 }
225
226 pub fn enter_scope(&mut self) {
228 self.scope_stack.push(std::collections::HashSet::new());
229 }
230
231 pub fn exit_scope(&mut self) {
233 if let Some(names) = self.scope_stack.pop() {
234 for name in names {
235 self.variables.remove(&name);
236 self.var_types.remove(&name);
237 self.alias_vars.remove(&name);
238 self.string_vars.remove(&name);
239 self.optimized_out_vars.remove(&name);
240 self.var_pc_addresses.remove(&name);
241 }
242 }
243 }
244
245 pub fn is_name_in_any_scope(&self, name: &str) -> bool {
247 self.scope_stack.iter().any(|s| s.contains(name))
248 }
249
250 pub fn is_name_in_current_scope(&self, name: &str) -> bool {
252 match self.scope_stack.last() {
253 Some(top) => top.contains(name),
254 None => false,
255 }
256 }
257
258 pub fn declare_name_in_current_scope(&mut self, name: &str) -> Result<()> {
260 if self.scope_stack.is_empty() {
261 self.enter_scope();
263 }
264 if self.is_name_in_current_scope(name) {
265 return Err(CodeGenError::TypeError(format!(
266 "Redeclaration in the same scope is not allowed: '{name}'"
267 )));
268 }
269 if self.is_name_in_any_scope(name) {
270 return Err(CodeGenError::TypeError(format!(
271 "Shadowing is not allowed for immutable variables: '{name}'"
272 )));
273 }
274 if let Some(top) = self.scope_stack.last_mut() {
275 top.insert(name.to_string());
276 }
277 Ok(())
278 }
279
280 pub fn new_with_process_analyzer(
282 context: &'ctx Context,
283 module_name: &str,
284 process_analyzer: Option<&mut DwarfAnalyzer>,
285 trace_id: Option<u32>,
286 compile_options: &crate::CompileOptions,
287 ) -> Result<Self> {
288 let mut codegen = Self::new(context, module_name, trace_id, compile_options)?;
289 codegen.process_analyzer = process_analyzer.map(|pa| pa as *const _ as *mut _);
290 Ok(codegen)
291 }
292
293 pub fn set_compile_time_context(&mut self, pc_address: u64, module_path: String) {
295 self.current_compile_time_context = Some(CompileTimeContext {
296 pc_address,
297 module_path,
298 });
299 }
300
301 pub fn get_compile_time_context(&self) -> Result<&CompileTimeContext> {
303 self.current_compile_time_context
304 .as_ref()
305 .ok_or_else(|| CodeGenError::DwarfError("No compile-time context set".to_string()))
306 }
307
308 pub fn take_module_hint(&mut self) -> Option<String> {
310 self.current_resolved_var_module_path.take()
311 }
312
313 fn declare_trace_printk(context: &'ctx Context, module: &Module<'ctx>) -> FunctionValue<'ctx> {
315 let i32_type = context.i32_type();
316 let ptr_type = context.ptr_type(AddressSpace::default());
317 let i64_type = context.i64_type();
318
319 let fn_type = i32_type.fn_type(&[ptr_type.into(), i64_type.into()], true);
321
322 module.add_function("bpf_trace_printk", fn_type, None)
323 }
324
325 pub fn create_basic_ebpf_function(&mut self, function_name: &str) -> Result<()> {
327 let i32_type = self.context.i32_type();
328 let ptr_type = self.context.ptr_type(AddressSpace::default());
329
330 let fn_type = i32_type.fn_type(&[ptr_type.into()], false);
332
333 let function = self.module.add_function(function_name, fn_type, None);
334
335 function.add_attribute(
337 inkwell::attributes::AttributeLoc::Function,
338 self.context.create_string_attribute("section", "uprobe"),
339 );
340
341 let basic_block = self.context.append_basic_block(function, "entry");
343 self.builder.position_at_end(basic_block);
344
345 info!("Created eBPF function: {}", function_name);
346 Ok(())
347 }
348
349 #[cfg(test)]
351 pub fn __test_ensure_proc_offsets_map(&mut self) -> Result<()> {
352 self.map_manager
353 .create_proc_module_offsets_map(
354 &self.module,
355 &self.di_builder,
356 &self.compile_unit,
357 "proc_module_offsets",
358 self.compile_options.proc_module_offsets_max_entries,
359 )
360 .map_err(|e| {
361 CodeGenError::LLVMError(format!(
362 "Failed to create proc_module_offsets map in test: {e}"
363 ))
364 })
365 }
366
367 #[cfg(test)]
369 pub fn __test_alloc_pm_key(&mut self) -> Result<()> {
370 let i32_type = self.context.i32_type();
371 let key_arr_ty = i32_type.array_type(4);
372 let key_alloca = self
373 .builder
374 .build_alloca(key_arr_ty, "pm_key")
375 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
376 self.pm_key_alloca = Some(key_alloca);
377 Ok(())
378 }
379
380 pub fn get_module(&self) -> &Module<'ctx> {
382 &self.module
383 }
384
385 pub fn get_trace_context(&self) -> ghostscope_protocol::TraceContext {
387 self.trace_context.clone()
388 }
389
390 pub fn get_pt_regs_parameter(&self) -> Result<PointerValue<'ctx>> {
392 let current_function = self
393 .builder
394 .get_insert_block()
395 .ok_or_else(|| CodeGenError::Builder("No current basic block".to_string()))?
396 .get_parent()
397 .ok_or_else(|| CodeGenError::Builder("No parent function".to_string()))?;
398
399 let pt_regs_param = current_function
400 .get_first_param()
401 .ok_or_else(|| CodeGenError::Builder("Function has no parameters".to_string()))?
402 .into_pointer_value();
403
404 Ok(pt_regs_param)
405 }
406
407 pub fn compile_program(
409 &mut self,
410 _program: &crate::script::Program,
411 function_name: &str,
412 trace_statements: &[crate::script::Statement],
413 target_pid: Option<u32>,
414 compile_time_pc: Option<u64>,
415 module_path: Option<&str>,
416 ) -> Result<(FunctionValue<'ctx>, ghostscope_protocol::TraceContext)> {
417 info!(
418 "Starting program compilation with function: {}",
419 function_name
420 );
421
422 self.current_compile_time_context =
424 if let (Some(pc), Some(path)) = (compile_time_pc, module_path) {
425 Some(CompileTimeContext {
426 pc_address: pc,
427 module_path: path.to_string(),
428 })
429 } else {
430 None
431 };
432
433 match self.compile_options.event_map_type {
436 crate::EventMapType::RingBuf => {
437 self.map_manager
438 .create_ringbuf_map(
439 &self.module,
440 &self.di_builder,
441 &self.compile_unit,
442 "ringbuf",
443 self.compile_options.ringbuf_size,
444 )
445 .map_err(|e| {
446 CodeGenError::LLVMError(format!("Failed to create ringbuf map: {e}"))
447 })?;
448 }
449 crate::EventMapType::PerfEventArray => {
450 self.map_manager
451 .create_perf_event_array_map(
452 &self.module,
453 &self.di_builder,
454 &self.compile_unit,
455 "events",
456 )
457 .map_err(|e| {
458 CodeGenError::LLVMError(format!(
459 "Failed to create perf event array map: {e}"
460 ))
461 })?;
462 }
463 }
464
465 self.map_manager
469 .create_percpu_array_map(
470 &self.module,
471 &self.di_builder,
472 &self.compile_unit,
473 "event_accum_buffer",
474 1,
475 self.compile_options.max_trace_event_size as u64,
476 )
477 .map_err(|e| {
478 CodeGenError::LLVMError(format!("Failed to create event_accum_buffer: {e}"))
479 })?;
480
481 self.map_manager
483 .create_proc_module_offsets_map(
484 &self.module,
485 &self.di_builder,
486 &self.compile_unit,
487 "proc_module_offsets",
488 self.compile_options.proc_module_offsets_max_entries,
489 )
490 .map_err(|e| {
491 CodeGenError::LLVMError(format!("Failed to create proc_module_offsets map: {e}"))
492 })?;
493
494 let main_function = self.create_main_function(function_name)?;
499
500 if let Some(pid) = target_pid {
502 self.add_pid_filter(pid)?;
503 }
504
505 let program = crate::script::ast::Program {
507 statements: trace_statements.to_vec(),
508 };
509
510 let variable_types = std::collections::HashMap::new(); let trace_context =
515 self.compile_program_with_staged_transmission(&program, variable_types)?;
516 info!(
517 "Generated TraceContext with {} strings",
518 trace_context.string_count()
519 );
520
521 let i32_type = self.context.i32_type();
523 let return_value = i32_type.const_int(0, false);
524 self.builder
525 .build_return(Some(&return_value))
526 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
527
528 info!(
529 "Successfully compiled program with function: {} and TraceContext",
530 function_name
531 );
532 Ok((main_function, trace_context))
533 }
534
535 fn create_main_function(&mut self, function_name: &str) -> Result<FunctionValue<'ctx>> {
537 let i32_type = self.context.i32_type();
538 let ptr_type = self.context.ptr_type(AddressSpace::default());
539
540 let fn_type = i32_type.fn_type(&[ptr_type.into()], false);
542 let function = self.module.add_function(function_name, fn_type, None);
543
544 function.set_section(Some("uprobe"));
546
547 let basic_block = self.context.append_basic_block(function, "entry");
549 self.builder.position_at_end(basic_block);
550
551 let key_arr_ty = i32_type.array_type(4);
554 let key_alloca = self
555 .builder
556 .build_alloca(key_arr_ty, "pm_key")
557 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
558 self.pm_key_alloca = Some(key_alloca);
559
560 let event_off_alloca = self
562 .builder
563 .build_alloca(i32_type, "event_offset")
564 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
565 self.builder
566 .build_store(event_off_alloca, i32_type.const_zero())
567 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
568 self.event_offset_alloca = Some(event_off_alloca);
569
570 info!("Created main function: {}", function_name);
571 Ok(function)
572 }
573
574 fn add_pid_filter(&mut self, target_pid: u32) -> Result<()> {
577 info!("Adding PID filter for target PID: {}", target_pid);
578
579 let current_fn = self
581 .builder
582 .get_insert_block()
583 .unwrap()
584 .get_parent()
585 .unwrap();
586
587 let continue_block = self
589 .context
590 .append_basic_block(current_fn, "continue_execution");
591 let early_return_block = self
592 .context
593 .append_basic_block(current_fn, "pid_mismatch_return");
594
595 let pid_tgid_value = self.get_current_pid_tgid()?;
597
598 let shift_amount = self.context.i64_type().const_int(32, false);
600 let current_tgid = self
601 .builder
602 .build_right_shift(pid_tgid_value, shift_amount, false, "current_tgid")
603 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
604
605 let target_pid_value = self.context.i64_type().const_int(target_pid as u64, false);
607 let pid_matches = self
608 .builder
609 .build_int_compare(
610 inkwell::IntPredicate::EQ,
611 current_tgid,
612 target_pid_value,
613 "pid_matches",
614 )
615 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
616
617 self.builder
619 .build_conditional_branch(pid_matches, continue_block, early_return_block)
620 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
621
622 self.builder.position_at_end(early_return_block);
624 self.builder
625 .build_return(Some(&self.context.i32_type().const_int(0, false)))
626 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
627
628 self.builder.position_at_end(continue_block);
630
631 info!(
632 "PID filter added successfully for target PID: {}",
633 target_pid
634 );
635 Ok(())
636 }
637
638 pub fn get_or_create_flag_global(&mut self, name: &str) -> PointerValue<'ctx> {
640 if let Some(g) = self.module.get_global(name) {
641 return g.as_pointer_value();
642 }
643 let i8_type = self.context.i8_type();
644 let global = self
645 .module
646 .add_global(i8_type, Some(AddressSpace::default()), name);
647 global.set_initializer(&i8_type.const_zero());
648 global.as_pointer_value()
649 }
650
651 pub fn store_flag_value(&mut self, name: &str, value: u8) -> Result<()> {
653 let ptr = self.get_or_create_flag_global(name);
654 self.builder
655 .build_store(ptr, self.context.i8_type().const_int(value as u64, false))
656 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store flag {name}: {e}")))?;
657 Ok(())
658 }
659
660 pub fn mark_any_success(&mut self) -> Result<()> {
662 self.store_flag_value("_gs_any_success", 1)
663 }
664
665 pub fn mark_any_fail(&mut self) -> Result<()> {
667 self.store_flag_value("_gs_any_fail", 1)
668 }
669
670 pub fn get_or_create_offsets_found_flag(&mut self) -> inkwell::values::PointerValue<'ctx> {
673 if let Some(ptr) = self.offsets_found_flag {
674 return ptr;
675 }
676 let i8_type = self.context.i8_type();
677 let global =
679 self.module
680 .add_global(i8_type, Some(AddressSpace::default()), "_gs_offsets_found");
681 global.set_initializer(&i8_type.const_int(1, false));
682 let ptr = global.as_pointer_value();
683 self.offsets_found_flag = Some(ptr);
684 ptr
685 }
686
687 pub fn store_offsets_found_flag(
689 &mut self,
690 flag: inkwell::values::IntValue<'ctx>,
691 ) -> Result<()> {
692 let ptr = self.get_or_create_offsets_found_flag();
693 let i8_type = self.context.i8_type();
694 let flag_i8 = self
695 .builder
696 .build_int_z_extend(flag, i8_type, "offset_flag_i8")
697 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
698 self.builder
699 .build_store(ptr, flag_i8)
700 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
701 Ok(())
702 }
703
704 pub fn store_offsets_found_const(&mut self, value: bool) -> Result<()> {
706 let ptr = self.get_or_create_offsets_found_flag();
707 let i8_type = self.context.i8_type();
708 self.builder
709 .build_store(ptr, i8_type.const_int(value as u64, false))
710 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
711 Ok(())
712 }
713
714 pub fn load_offsets_found_flag(&mut self) -> Result<inkwell::values::IntValue<'ctx>> {
716 let ptr = self.get_or_create_offsets_found_flag();
717 let i8_type = self.context.i8_type();
718 let raw = self
719 .builder
720 .build_load(i8_type, ptr, "offsets_found_raw")
721 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
722 .into_int_value();
723 let is_non_zero = self
724 .builder
725 .build_int_compare(
726 inkwell::IntPredicate::NE,
727 raw,
728 i8_type.const_zero(),
729 "offsets_found_bool",
730 )
731 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
732 Ok(is_non_zero)
733 }
734
735 pub fn get_or_create_cond_error_global(&mut self) -> PointerValue<'ctx> {
737 if let Some(g) = self.module.get_global("_gs_cond_error") {
738 return g.as_pointer_value();
739 }
740 let i8_type = self.context.i8_type();
741 let global =
742 self.module
743 .add_global(i8_type, Some(AddressSpace::default()), "_gs_cond_error");
744 global.set_initializer(&i8_type.const_zero());
745 global.as_pointer_value()
746 }
747
748 pub fn reset_condition_error(&mut self) -> Result<()> {
750 let ptr = self.get_or_create_cond_error_global();
751 self.builder
752 .build_store(ptr, self.context.i8_type().const_zero())
753 .map_err(|e| CodeGenError::LLVMError(format!("Failed to reset _gs_cond_error: {e}")))?;
754 let aptr = self.get_or_create_cond_error_addr_global();
756 self.builder
757 .build_store(aptr, self.context.i64_type().const_zero())
758 .map_err(|e| {
759 CodeGenError::LLVMError(format!("Failed to reset _gs_cond_error_addr: {e}"))
760 })?;
761 let fptr = self.get_or_create_cond_error_flags_global();
763 self.builder
764 .build_store(fptr, self.context.i8_type().const_zero())
765 .map_err(|e| {
766 CodeGenError::LLVMError(format!("Failed to reset _gs_cond_error_flags: {e}"))
767 })?;
768 Ok(())
769 }
770
771 pub fn get_or_create_cond_error_addr_global(&mut self) -> PointerValue<'ctx> {
773 if let Some(g) = self.module.get_global("_gs_cond_error_addr") {
774 return g.as_pointer_value();
775 }
776 let i64_type = self.context.i64_type();
777 let global = self.module.add_global(
778 i64_type,
779 Some(AddressSpace::default()),
780 "_gs_cond_error_addr",
781 );
782 global.set_initializer(&i64_type.const_zero());
783 global.as_pointer_value()
784 }
785
786 pub fn set_condition_error_if_unset(&mut self, code: u8) -> Result<()> {
788 if !self.condition_context_active {
789 return Ok(());
790 }
791 let ptr = self.get_or_create_cond_error_global();
792 let cur = self
793 .builder
794 .build_load(self.context.i8_type(), ptr, "cond_err_cur")
795 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
796 .into_int_value();
797 let is_zero = self
798 .builder
799 .build_int_compare(
800 inkwell::IntPredicate::EQ,
801 cur,
802 self.context.i8_type().const_zero(),
803 "cond_err_is_zero",
804 )
805 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
806 let newv_bv: inkwell::values::BasicValueEnum =
807 self.context.i8_type().const_int(code as u64, false).into();
808 let sel = self
809 .builder
810 .build_select::<inkwell::values::BasicValueEnum, _>(
811 is_zero,
812 newv_bv,
813 cur.into(),
814 "cond_err_new",
815 )
816 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
817 self.builder
818 .build_store(ptr, sel)
819 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
820 Ok(())
821 }
822
823 pub fn build_condition_error_predicate(&mut self) -> Result<inkwell::values::IntValue<'ctx>> {
825 let ptr = self.get_or_create_cond_error_global();
826 let cur = self
827 .builder
828 .build_load(self.context.i8_type(), ptr, "cond_err_cur")
829 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
830 .into_int_value();
831 self.builder
832 .build_int_compare(
833 inkwell::IntPredicate::NE,
834 cur,
835 self.context.i8_type().const_zero(),
836 "cond_err_nonzero",
837 )
838 .map_err(|e| CodeGenError::LLVMError(e.to_string()))
839 }
840
841 pub fn get_or_create_cond_error_flags_global(&mut self) -> PointerValue<'ctx> {
843 if let Some(g) = self.module.get_global("_gs_cond_error_flags") {
844 return g.as_pointer_value();
845 }
846 let i8_type = self.context.i8_type();
847 let global = self.module.add_global(
848 i8_type,
849 Some(AddressSpace::default()),
850 "_gs_cond_error_flags",
851 );
852 global.set_initializer(&i8_type.const_zero());
853 global.as_pointer_value()
854 }
855
856 pub fn or_condition_error_flags(&mut self, flags: IntValue<'ctx>) -> Result<()> {
858 if !self.condition_context_active {
859 return Ok(());
860 }
861 let ptr = self.get_or_create_cond_error_flags_global();
862 let cur = self
863 .builder
864 .build_load(self.context.i8_type(), ptr, "cond_err_flags_cur")
865 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
866 .into_int_value();
867 let newv = self
868 .builder
869 .build_or(cur, flags, "cond_err_flags_or")
870 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
871 self.builder
872 .build_store(ptr, newv)
873 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
874 Ok(())
875 }
876
877 pub fn set_condition_error_addr_if_unset(&mut self, addr: IntValue<'ctx>) -> Result<()> {
879 if !self.condition_context_active {
880 return Ok(());
881 }
882 let ptr = self.get_or_create_cond_error_addr_global();
883 let cur = self
884 .builder
885 .build_load(self.context.i64_type(), ptr, "cond_err_addr_cur")
886 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
887 .into_int_value();
888 let is_zero = self
889 .builder
890 .build_int_compare(
891 inkwell::IntPredicate::EQ,
892 cur,
893 self.context.i64_type().const_zero(),
894 "cond_err_addr_is_zero",
895 )
896 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
897 let sel = self
898 .builder
899 .build_select::<IntValue<'ctx>, _>(is_zero, addr, cur, "cond_err_addr_new")
900 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
901 self.builder
902 .build_store(ptr, sel)
903 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
904 Ok(())
905 }
906}