1use super::context::{CodeGenError, EbpfContext, Result};
7use crate::script::{PrintStatement, Program, Statement};
8use aya_ebpf_bindings::bindings::bpf_func_id::BPF_FUNC_probe_read_user;
9use ghostscope_protocol::trace_event::{
10 BacktraceData, InstructionHeader, PrintComplexVariableData, PrintStringIndexData,
11 PrintVariableIndexData, VariableStatus,
12};
13use ghostscope_protocol::{InstructionType, TraceContext, TypeKind};
14use inkwell::values::{BasicValueEnum, IntValue};
15use inkwell::AddressSpace;
16use std::collections::HashMap;
17use tracing::{debug, info, warn};
18
19#[derive(Debug, Clone)]
21struct PrintVarRuntimeMeta {
22 var_name_index: u16,
23 type_index: u16,
24 access_path: String,
25 data_len_limit: usize,
26}
27
28#[derive(Debug, Clone)]
30enum ComplexArgSource<'ctx> {
31 RuntimeRead {
32 eval_result: ghostscope_dwarf::EvaluationResult,
33 dwarf_type: ghostscope_dwarf::TypeInfo,
34 module_for_offsets: Option<String>,
35 },
36 MemDump {
38 src_addr: inkwell::values::IntValue<'ctx>,
39 len: usize,
40 },
41 MemDumpDynamic {
43 src_addr: inkwell::values::IntValue<'ctx>,
44 len_value: inkwell::values::IntValue<'ctx>,
45 max_len: usize,
46 },
47 ImmediateBytes {
48 bytes: Vec<u8>,
49 },
50 AddressValue {
51 eval_result: ghostscope_dwarf::EvaluationResult,
52 module_for_offsets: Option<String>,
53 },
54 ComputedInt {
56 value: inkwell::values::IntValue<'ctx>,
57 byte_len: usize, },
59}
60
61#[derive(Debug, Clone)]
63struct ComplexArg<'ctx> {
64 var_name_index: u16,
65 type_index: u16,
66 access_path: Vec<u8>,
67 data_len: usize,
68 source: ComplexArgSource<'ctx>,
69}
70
71impl<'ctx> EbpfContext<'ctx> {
72 fn resolve_expr_to_arg(&mut self, expr: &crate::script::ast::Expr) -> Result<ComplexArg<'ctx>> {
76 use crate::script::ast::Expr as E;
77 match expr {
78 E::Variable(name) if self.alias_variable_exists(name) => {
80 let aliased = self.get_alias_variable(name).expect("alias exists");
81 let addr_i64 = self.resolve_ptr_i64_from_expr(&aliased)?;
82 let var_name_index = self.trace_context.add_variable_name(name.clone());
83 Ok(ComplexArg {
84 var_name_index,
85 type_index: self.add_synthesized_type_index_for_kind(TypeKind::Pointer),
86 access_path: Vec::new(),
87 data_len: 8,
88 source: ComplexArgSource::ComputedInt {
89 value: addr_i64,
90 byte_len: 8,
91 },
92 })
93 }
94 E::Variable(name) if self.variable_exists(name) => {
96 let val = self.load_variable(name)?;
97 let var_name_index = self.trace_context.add_variable_name(name.clone());
98 if self
100 .get_variable_type(name)
101 .is_some_and(|t| matches!(t, crate::script::VarType::String))
102 {
103 let bytes_opt = self.get_string_variable_bytes(name).cloned();
104 if let Some(bytes) = bytes_opt {
105 let char_type = ghostscope_dwarf::TypeInfo::BaseType {
107 name: "char".to_string(),
108 size: 1,
109 encoding: ghostscope_dwarf::constants::DW_ATE_unsigned_char.0 as u16,
110 };
111 let array_type = ghostscope_dwarf::TypeInfo::ArrayType {
112 element_type: Box::new(char_type),
113 element_count: Some(bytes.len() as u64),
114 total_size: Some(bytes.len() as u64),
115 };
116 return Ok(ComplexArg {
117 var_name_index,
118 type_index: self.trace_context.add_type(array_type),
119 access_path: Vec::new(),
120 data_len: bytes.len(),
121 source: ComplexArgSource::ImmediateBytes { bytes },
122 });
123 }
124 }
125 match val {
126 BasicValueEnum::IntValue(iv) => {
127 let bitw = iv.get_type().get_bit_width();
129 let (kind, byte_len) = if bitw == 1 {
130 (TypeKind::Bool, 1)
131 } else if bitw <= 8 {
132 (TypeKind::I8, 1)
133 } else if bitw <= 16 {
134 (TypeKind::I16, 2)
135 } else if bitw <= 32 {
136 (TypeKind::I32, 4)
137 } else {
138 (TypeKind::I64, 8)
139 };
140 Ok(ComplexArg {
141 var_name_index,
142 type_index: self.add_synthesized_type_index_for_kind(kind),
143 access_path: Vec::new(),
144 data_len: byte_len,
145 source: ComplexArgSource::ComputedInt {
146 value: iv,
147 byte_len,
148 },
149 })
150 }
151 BasicValueEnum::PointerValue(pv) => {
152 let iv = self
154 .builder
155 .build_ptr_to_int(pv, self.context.i64_type(), "ptr_to_i64")
156 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
157 Ok(ComplexArg {
158 var_name_index,
159 type_index: self.add_synthesized_type_index_for_kind(TypeKind::Pointer),
160 access_path: Vec::new(),
161 data_len: 8,
162 source: ComplexArgSource::ComputedInt {
163 value: iv,
164 byte_len: 8,
165 },
166 })
167 }
168 _ => Err(CodeGenError::TypeError(
169 "Unsupported script variable type for print".to_string(),
170 )),
171 }
172 }
173
174 E::String(s) => {
176 let mut bytes = s.as_bytes().to_vec();
177 bytes.push(0);
178 let char_type = ghostscope_dwarf::TypeInfo::BaseType {
179 name: "char".to_string(),
180 size: 1,
181 encoding: ghostscope_dwarf::constants::DW_ATE_unsigned_char.0 as u16,
182 };
183 let array_type = ghostscope_dwarf::TypeInfo::ArrayType {
184 element_type: Box::new(char_type),
185 element_count: Some(bytes.len() as u64),
186 total_size: Some(bytes.len() as u64),
187 };
188 Ok(ComplexArg {
189 var_name_index: self
190 .trace_context
191 .add_variable_name("__str_literal".to_string()),
192 type_index: self.trace_context.add_type(array_type),
193 access_path: Vec::new(),
194 data_len: bytes.len(),
195 source: ComplexArgSource::ImmediateBytes { bytes },
196 })
197 }
198
199 E::Int(v) => {
201 let mut bytes = Vec::with_capacity(8);
202 bytes.extend_from_slice(&(*v).to_le_bytes());
203 let int_type = ghostscope_dwarf::TypeInfo::BaseType {
204 name: "i64".to_string(),
205 size: 8,
206 encoding: ghostscope_dwarf::constants::DW_ATE_signed.0 as u16,
207 };
208 Ok(ComplexArg {
209 var_name_index: self
210 .trace_context
211 .add_variable_name("__int_literal".to_string()),
212 type_index: self.trace_context.add_type(int_type),
213 access_path: Vec::new(),
214 data_len: 8,
215 source: ComplexArgSource::ImmediateBytes { bytes },
216 })
217 }
218
219 E::AddressOf(inner) => {
221 let var = self
222 .query_dwarf_for_complex_expr(inner)?
223 .ok_or_else(|| CodeGenError::VariableNotFound(format!("{inner:?}")))?;
224 let inner_ty = var.dwarf_type.as_ref().ok_or_else(|| {
225 CodeGenError::DwarfError("Expression has no DWARF type information".to_string())
226 })?;
227 let ptr_ty = ghostscope_dwarf::TypeInfo::PointerType {
228 target_type: Box::new(inner_ty.clone()),
229 size: 8,
230 };
231 let module_hint = self.take_module_hint();
232 Ok(ComplexArg {
233 var_name_index: self
234 .trace_context
235 .add_variable_name(self.expr_to_name(expr)),
236 type_index: self.trace_context.add_type(ptr_ty),
237 access_path: Vec::new(),
238 data_len: 8,
239 source: ComplexArgSource::AddressValue {
240 eval_result: var.evaluation_result.clone(),
241 module_for_offsets: module_hint,
242 },
243 })
244 }
245
246 expr @ (E::MemberAccess(_, _)
248 | E::ArrayAccess(_, _)
249 | E::PointerDeref(_)
250 | E::ChainAccess(_)) => {
251 let var = self
252 .query_dwarf_for_complex_expr(expr)?
253 .ok_or_else(|| CodeGenError::VariableNotFound(format!("{expr:?}")))?;
254 if matches!(
255 var.evaluation_result,
256 ghostscope_dwarf::EvaluationResult::Optimized
257 ) {
258 let ti = ghostscope_protocol::type_info::TypeInfo::OptimizedOut {
259 name: var.name.clone(),
260 };
261 return Ok(ComplexArg {
262 var_name_index: self.trace_context.add_variable_name(var.name.clone()),
263 type_index: self.trace_context.add_type(ti),
264 access_path: Vec::new(),
265 data_len: 0,
266 source: ComplexArgSource::ImmediateBytes { bytes: Vec::new() },
267 });
268 }
269 let dwarf_type = var.dwarf_type.as_ref().ok_or_else(|| {
270 CodeGenError::DwarfError("Expression has no DWARF type information".to_string())
271 })?;
272 let data_len = Self::compute_read_size_for_type(dwarf_type);
273 if data_len == 0 {
274 return Err(CodeGenError::TypeSizeNotAvailable(var.name));
275 }
276 let module_hint = self.take_module_hint();
279 Ok(ComplexArg {
280 var_name_index: self.trace_context.add_variable_name(var.name.clone()),
281 type_index: self.trace_context.add_type(dwarf_type.clone()),
282 access_path: Vec::new(),
283 data_len,
284 source: ComplexArgSource::RuntimeRead {
285 eval_result: var.evaluation_result.clone(),
286 dwarf_type: dwarf_type.clone(),
287 module_for_offsets: module_hint,
288 },
289 })
290 }
291
292 E::Variable(name) => {
294 if let Some(v) = self.query_dwarf_for_variable(name)? {
295 if let Some(ref t) = v.dwarf_type {
296 if matches!(
298 v.evaluation_result,
299 ghostscope_dwarf::EvaluationResult::Optimized
300 ) {
301 let ti = ghostscope_protocol::type_info::TypeInfo::OptimizedOut {
302 name: v.name.clone(),
303 };
304 return Ok(ComplexArg {
305 var_name_index: self
306 .trace_context
307 .add_variable_name(v.name.clone()),
308 type_index: self.trace_context.add_type(ti),
309 access_path: Vec::new(),
310 data_len: 0,
311 source: ComplexArgSource::ImmediateBytes { bytes: Vec::new() },
312 });
313 }
314 let is_link_addr = matches!(
315 v.evaluation_result,
316 ghostscope_dwarf::EvaluationResult::MemoryLocation(
317 ghostscope_dwarf::LocationResult::Address(_)
318 )
319 );
320 if Self::is_simple_typeinfo(t) && !is_link_addr {
321 let compiled = self.compile_expr(expr)?;
323 match compiled {
324 BasicValueEnum::IntValue(iv) => {
325 let (kind, byte_len) = if matches!(
327 t,
328 ghostscope_dwarf::TypeInfo::PointerType { .. }
329 ) {
330 (TypeKind::Pointer, 8)
331 } else {
332 let bitw = iv.get_type().get_bit_width();
333 if bitw == 1 {
334 (TypeKind::Bool, 1)
335 } else if bitw <= 8 {
336 (TypeKind::I8, 1)
337 } else if bitw <= 16 {
338 (TypeKind::I16, 2)
339 } else if bitw <= 32 {
340 (TypeKind::I32, 4)
341 } else {
342 (TypeKind::I64, 8)
343 }
344 };
345 Ok(ComplexArg {
346 var_name_index: self
347 .trace_context
348 .add_variable_name(self.expr_to_name(expr)),
349 type_index: self.add_synthesized_type_index_for_kind(kind),
350 access_path: Vec::new(),
351 data_len: byte_len,
352 source: ComplexArgSource::ComputedInt {
353 value: iv,
354 byte_len,
355 },
356 })
357 }
358 BasicValueEnum::PointerValue(pv) => {
359 let iv = self
361 .builder
362 .build_ptr_to_int(pv, self.context.i64_type(), "ptr_to_i64")
363 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
364 Ok(ComplexArg {
365 var_name_index: self
366 .trace_context
367 .add_variable_name(self.expr_to_name(expr)),
368 type_index: self
369 .add_synthesized_type_index_for_kind(TypeKind::Pointer),
370 access_path: Vec::new(),
371 data_len: 8,
372 source: ComplexArgSource::ComputedInt {
373 value: iv,
374 byte_len: 8,
375 },
376 })
377 }
378 _ => {
379 let data_len = Self::compute_read_size_for_type(t);
381 if data_len == 0 {
382 return Err(CodeGenError::TypeSizeNotAvailable(v.name));
383 }
384 let module_hint = self.take_module_hint();
385 Ok(ComplexArg {
386 var_name_index: self
387 .trace_context
388 .add_variable_name(v.name.clone()),
389 type_index: self.trace_context.add_type(t.clone()),
390 access_path: Vec::new(),
391 data_len,
392 source: ComplexArgSource::RuntimeRead {
393 eval_result: v.evaluation_result.clone(),
394 dwarf_type: t.clone(),
395 module_for_offsets: module_hint,
396 },
397 })
398 }
399 }
400 } else {
401 let data_len = Self::compute_read_size_for_type(t);
404 if data_len == 0 {
405 return Err(CodeGenError::TypeSizeNotAvailable(v.name));
406 }
407 let module_hint = self.take_module_hint();
408 Ok(ComplexArg {
409 var_name_index: self
410 .trace_context
411 .add_variable_name(v.name.clone()),
412 type_index: self.trace_context.add_type(t.clone()),
413 access_path: Vec::new(),
414 data_len,
415 source: ComplexArgSource::RuntimeRead {
416 eval_result: v.evaluation_result.clone(),
417 dwarf_type: t.clone(),
418 module_for_offsets: module_hint,
419 },
420 })
421 }
422 } else {
423 Err(CodeGenError::DwarfError(
424 "Variable has no DWARF type information".to_string(),
425 ))
426 }
427 } else {
428 Err(CodeGenError::VariableNotInScope(name.clone()))
429 }
430 }
431
432 E::BinaryOp { left, op, right } => {
434 use crate::script::ast::BinaryOp as BO;
435 let (ptr_side, int_side, sign) = match (&**left, op, &**right) {
439 (l, BO::Add, E::Int(k)) => (l, *k, 1),
440 (E::Int(k), BO::Add, r) => (r, *k, 1),
441 (l, BO::Subtract, E::Int(k)) => (l, *k, -1),
442 _ => {
443 let compiled = self.compile_expr(expr)?;
445 if let BasicValueEnum::IntValue(iv) = compiled {
446 let bitw = iv.get_type().get_bit_width();
447 let (kind, byte_len) = if bitw == 1 {
448 (TypeKind::Bool, 1)
449 } else if bitw <= 8 {
450 (TypeKind::I8, 1)
451 } else if bitw <= 16 {
452 (TypeKind::I16, 2)
453 } else if bitw <= 32 {
454 (TypeKind::I32, 4)
455 } else {
456 (TypeKind::I64, 8)
457 };
458 return Ok(ComplexArg {
459 var_name_index: self
460 .trace_context
461 .add_variable_name(self.expr_to_name(expr)),
462 type_index: self.add_synthesized_type_index_for_kind(kind),
463 access_path: Vec::new(),
464 data_len: byte_len,
465 source: ComplexArgSource::ComputedInt {
466 value: iv,
467 byte_len,
468 },
469 });
470 } else {
471 return Err(CodeGenError::TypeError(
472 "Non-integer expression not supported in print".to_string(),
473 ));
474 }
475 }
476 };
477
478 if let Some(var) = self.query_dwarf_for_complex_expr(ptr_side)? {
480 if var.dwarf_type.is_some() {
481 let index = sign * int_side;
483 let (eval_result, elem_ty) =
484 self.compute_pointed_location_with_index(ptr_side, index)?;
485 let data_len = Self::compute_read_size_for_type(&elem_ty);
486 let module_hint = self.take_module_hint();
487 if data_len == 0 {
488 let ptr_ti = ghostscope_dwarf::TypeInfo::PointerType {
490 target_type: Box::new(elem_ty.clone()),
491 size: 8,
492 };
493 return Ok(ComplexArg {
494 var_name_index: self
495 .trace_context
496 .add_variable_name(self.expr_to_name(expr)),
497 type_index: self.trace_context.add_type(ptr_ti),
498 access_path: Vec::new(),
499 data_len: 8,
500 source: ComplexArgSource::AddressValue {
501 eval_result,
502 module_for_offsets: module_hint,
503 },
504 });
505 }
506 return Ok(ComplexArg {
507 var_name_index: self
508 .trace_context
509 .add_variable_name(self.expr_to_name(expr)),
510 type_index: self.trace_context.add_type(elem_ty.clone()),
511 access_path: Vec::new(),
512 data_len,
513 source: ComplexArgSource::RuntimeRead {
514 eval_result,
515 dwarf_type: elem_ty,
516 module_for_offsets: module_hint,
517 },
518 });
519 }
520 }
521
522 let compiled = self.compile_expr(expr)?;
524 if let BasicValueEnum::IntValue(iv) = compiled {
525 let bitw = iv.get_type().get_bit_width();
526 let (kind, byte_len) = if bitw == 1 {
527 (TypeKind::Bool, 1)
528 } else if bitw <= 8 {
529 (TypeKind::I8, 1)
530 } else if bitw <= 16 {
531 (TypeKind::I16, 2)
532 } else if bitw <= 32 {
533 (TypeKind::I32, 4)
534 } else {
535 (TypeKind::I64, 8)
536 };
537 Ok(ComplexArg {
538 var_name_index: self
539 .trace_context
540 .add_variable_name(self.expr_to_name(expr)),
541 type_index: self.add_synthesized_type_index_for_kind(kind),
542 access_path: Vec::new(),
543 data_len: byte_len,
544 source: ComplexArgSource::ComputedInt {
545 value: iv,
546 byte_len,
547 },
548 })
549 } else {
550 Err(CodeGenError::TypeError(
551 "Non-integer expression not supported in print".to_string(),
552 ))
553 }
554 }
555
556 other => {
558 let compiled = self.compile_expr(other)?;
559 if let BasicValueEnum::IntValue(iv) = compiled {
560 let bitw = iv.get_type().get_bit_width();
561 let (kind, byte_len) = if bitw == 1 {
562 (TypeKind::Bool, 1)
563 } else if bitw <= 8 {
564 (TypeKind::I8, 1)
565 } else if bitw <= 16 {
566 (TypeKind::I16, 2)
567 } else if bitw <= 32 {
568 (TypeKind::I32, 4)
569 } else {
570 (TypeKind::I64, 8)
571 };
572 Ok(ComplexArg {
573 var_name_index: self
574 .trace_context
575 .add_variable_name(self.expr_to_name(other)),
576 type_index: self.add_synthesized_type_index_for_kind(kind),
577 access_path: Vec::new(),
578 data_len: byte_len,
579 source: ComplexArgSource::ComputedInt {
580 value: iv,
581 byte_len,
582 },
583 })
584 } else {
585 Err(CodeGenError::TypeError(
586 "Non-integer expression not supported in print".to_string(),
587 ))
588 }
589 }
590 }
591 }
592
593 fn emit_print_from_arg(&mut self, arg: ComplexArg<'ctx>) -> Result<u16> {
595 match arg.source {
596 ComplexArgSource::ComputedInt { value, byte_len } => {
597 self.generate_print_complex_variable_computed(
598 arg.var_name_index,
599 arg.type_index,
600 byte_len,
601 value,
602 )?;
603 Ok(1)
604 }
605 ComplexArgSource::RuntimeRead {
606 eval_result,
607 ref dwarf_type,
608 module_for_offsets,
609 } => {
610 let meta = PrintVarRuntimeMeta {
611 var_name_index: arg.var_name_index,
612 type_index: arg.type_index,
613 access_path: String::new(),
614 data_len_limit: arg.data_len,
615 };
616 self.generate_print_complex_variable_runtime(
617 meta,
618 &eval_result,
619 dwarf_type,
620 module_for_offsets.as_deref(),
621 )?;
622 Ok(1)
623 }
624 ComplexArgSource::AddressValue { .. } | ComplexArgSource::ImmediateBytes { .. } => {
625 let fmt_idx = self.trace_context.add_string("{}".to_string());
627 self.generate_print_complex_format_instruction(fmt_idx, &[arg])?;
628 Ok(1)
629 }
630 ComplexArgSource::MemDump { .. } | ComplexArgSource::MemDumpDynamic { .. } => {
631 let fmt_idx = self.trace_context.add_string("{}".to_string());
633 self.generate_print_complex_format_instruction(fmt_idx, &[arg])?;
634 Ok(1)
635 }
636 }
637 }
638 fn generate_print_complex_variable_computed(
641 &mut self,
642 var_name_index: u16,
643 type_index: u16,
644 byte_len: usize,
645 value: IntValue<'ctx>,
646 ) -> Result<()> {
647 let header_size = std::mem::size_of::<InstructionHeader>();
649 let data_struct_size = std::mem::size_of::<PrintComplexVariableData>();
650 let access_path_len: usize = 0; let total_data_length = data_struct_size + access_path_len + byte_len;
652 let total_size = header_size + total_data_length;
653
654 let inst_buffer = self.reserve_instruction_region(total_size as u64);
656
657 let inst_type_val = self
659 .context
660 .i8_type()
661 .const_int(InstructionType::PrintComplexVariable as u64, false);
662 self.builder
663 .build_store(inst_buffer, inst_type_val)
664 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store inst_type: {e}")))?;
665
666 let data_length_ptr = unsafe {
668 self.builder
669 .build_gep(
670 self.context.i8_type(),
671 inst_buffer,
672 &[self.context.i32_type().const_int(1, false)],
673 "data_length_ptr",
674 )
675 .map_err(|e| {
676 CodeGenError::LLVMError(format!("Failed to get data_length GEP: {e}"))
677 })?
678 };
679 let data_length_ptr_cast = self
680 .builder
681 .build_pointer_cast(
682 data_length_ptr,
683 self.context.ptr_type(AddressSpace::default()),
684 "data_length_ptr_cast",
685 )
686 .map_err(|e| CodeGenError::LLVMError(format!("Failed to cast data_length ptr: {e}")))?;
687 self.builder
688 .build_store(
689 data_length_ptr_cast,
690 self.context
691 .i16_type()
692 .const_int(total_data_length as u64, false),
693 )
694 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store data_length: {e}")))?;
695
696 let data_ptr = unsafe {
698 self.builder
699 .build_gep(
700 self.context.i8_type(),
701 inst_buffer,
702 &[self.context.i32_type().const_int(header_size as u64, false)],
703 "data_ptr",
704 )
705 .map_err(|e| CodeGenError::LLVMError(format!("Failed to get data GEP: {e}")))?
706 };
707
708 let var_name_index_val = self
710 .context
711 .i16_type()
712 .const_int(var_name_index as u64, false);
713 let var_name_index_off =
714 std::mem::offset_of!(PrintComplexVariableData, var_name_index) as u64;
715 let var_name_index_ptr_i8 = unsafe {
716 self.builder
717 .build_gep(
718 self.context.i8_type(),
719 data_ptr,
720 &[self.context.i32_type().const_int(var_name_index_off, false)],
721 "var_name_index_ptr_i8",
722 )
723 .map_err(|e| {
724 CodeGenError::LLVMError(format!("Failed to get var_name_index GEP: {e}"))
725 })?
726 };
727 let var_name_index_ptr_i16 = self
728 .builder
729 .build_pointer_cast(
730 var_name_index_ptr_i8,
731 self.context.ptr_type(AddressSpace::default()),
732 "var_name_index_ptr_i16",
733 )
734 .map_err(|e| {
735 CodeGenError::LLVMError(format!("Failed to cast var_name_index ptr: {e}"))
736 })?;
737 self.builder
738 .build_store(var_name_index_ptr_i16, var_name_index_val)
739 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store var_name_index: {e}")))?;
740
741 let type_index_offset = std::mem::offset_of!(PrintComplexVariableData, type_index) as u64;
743 let type_index_ptr_i8 = unsafe {
744 self.builder
745 .build_gep(
746 self.context.i8_type(),
747 data_ptr,
748 &[self.context.i32_type().const_int(type_index_offset, false)],
749 "type_index_ptr_i8",
750 )
751 .map_err(|e| {
752 CodeGenError::LLVMError(format!("Failed to get type_index GEP: {e}"))
753 })?
754 };
755 let type_index_ptr = self
756 .builder
757 .build_pointer_cast(
758 type_index_ptr_i8,
759 self.context.ptr_type(AddressSpace::default()),
760 "type_index_ptr_i16",
761 )
762 .map_err(|e| CodeGenError::LLVMError(format!("Failed to cast type_index ptr: {e}")))?;
763 let type_index_val = self.context.i16_type().const_int(type_index as u64, false);
764 self.builder
765 .build_store(type_index_ptr, type_index_val)
766 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store type_index: {e}")))?;
767
768 let access_path_len_off =
770 std::mem::offset_of!(PrintComplexVariableData, access_path_len) as u64;
771 let access_path_len_ptr = unsafe {
772 self.builder
773 .build_gep(
774 self.context.i8_type(),
775 data_ptr,
776 &[self
777 .context
778 .i32_type()
779 .const_int(access_path_len_off, false)],
780 "access_path_len_ptr",
781 )
782 .map_err(|e| {
783 CodeGenError::LLVMError(format!("Failed to get access_path_len GEP: {e}"))
784 })?
785 };
786 self.builder
787 .build_store(access_path_len_ptr, self.context.i8_type().const_zero())
788 .map_err(|e| {
789 CodeGenError::LLVMError(format!("Failed to store access_path_len: {e}"))
790 })?;
791
792 let status_off = std::mem::offset_of!(PrintComplexVariableData, status) as u64;
794 let status_ptr = unsafe {
795 self.builder
796 .build_gep(
797 self.context.i8_type(),
798 data_ptr,
799 &[self.context.i32_type().const_int(status_off, false)],
800 "status_ptr",
801 )
802 .map_err(|e| CodeGenError::LLVMError(format!("Failed to get status GEP: {e}")))?
803 };
804 self.builder
805 .build_store(status_ptr, self.context.i8_type().const_zero())
806 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store status: {e}")))?;
807
808 let data_len_off = std::mem::offset_of!(PrintComplexVariableData, data_len) as u64;
810 let data_len_ptr = unsafe {
811 self.builder
812 .build_gep(
813 self.context.i8_type(),
814 data_ptr,
815 &[self.context.i32_type().const_int(data_len_off, false)],
816 "data_len_ptr",
817 )
818 .map_err(|e| CodeGenError::LLVMError(format!("Failed to get data_len GEP: {e}")))?
819 };
820 let data_len_ptr_cast = self
821 .builder
822 .build_pointer_cast(
823 data_len_ptr,
824 self.context.ptr_type(AddressSpace::default()),
825 "data_len_ptr_cast",
826 )
827 .map_err(|e| CodeGenError::LLVMError(format!("Failed to cast data_len ptr: {e}")))?;
828 self.builder
829 .build_store(
830 data_len_ptr_cast,
831 self.context.i16_type().const_int(byte_len as u64, false),
832 )
833 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store data_len: {e}")))?;
834
835 let var_data_ptr = unsafe {
837 self.builder
838 .build_gep(
839 self.context.i8_type(),
840 data_ptr,
841 &[self
842 .context
843 .i32_type()
844 .const_int(data_struct_size as u64, false)],
845 "var_data_ptr",
846 )
847 .map_err(|e| CodeGenError::LLVMError(format!("Failed to get var_data GEP: {e}")))?
848 };
849
850 match byte_len {
852 1 => {
853 let bitw = value.get_type().get_bit_width();
854 let v = if bitw == 1 {
855 self.builder
857 .build_int_z_extend(value, self.context.i8_type(), "expr_zext_bool_i8")
858 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
859 } else if bitw < 8 {
860 self.builder
861 .build_int_s_extend(value, self.context.i8_type(), "expr_sext_i8")
862 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
863 } else if bitw > 8 {
864 self.builder
865 .build_int_truncate(value, self.context.i8_type(), "expr_trunc_i8")
866 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
867 } else {
868 value
869 };
870 self.builder
871 .build_store(var_data_ptr, v)
872 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
873 }
874 2 => {
875 let bitw = value.get_type().get_bit_width();
876 let v = if bitw < 16 {
877 self.builder
878 .build_int_s_extend(value, self.context.i16_type(), "expr_sext_i16")
879 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
880 } else if bitw > 16 {
881 self.builder
882 .build_int_truncate(value, self.context.i16_type(), "expr_trunc_i16")
883 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
884 } else {
885 value
886 };
887 let i16_ptr_ty = self.context.ptr_type(AddressSpace::default());
888 let cast_ptr = self
889 .builder
890 .build_pointer_cast(var_data_ptr, i16_ptr_ty, "expr_i16_ptr")
891 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
892 self.builder
893 .build_store(cast_ptr, v)
894 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
895 }
896 4 => {
897 let bitw = value.get_type().get_bit_width();
898 let v = if bitw < 32 {
899 self.builder
900 .build_int_s_extend(value, self.context.i32_type(), "expr_sext_i32")
901 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
902 } else if bitw > 32 {
903 self.builder
904 .build_int_truncate(value, self.context.i32_type(), "expr_trunc_i32")
905 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
906 } else {
907 value
908 };
909 let i32_ptr_ty = self.context.ptr_type(AddressSpace::default());
910 let cast_ptr = self
911 .builder
912 .build_pointer_cast(var_data_ptr, i32_ptr_ty, "expr_i32_ptr")
913 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
914 self.builder
915 .build_store(cast_ptr, v)
916 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
917 }
918 8 => {
919 let v64 = if value.get_type().get_bit_width() < 64 {
920 self.builder
921 .build_int_s_extend(value, self.context.i64_type(), "expr_sext_i64")
922 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
923 } else {
924 value
925 };
926 let i64_ptr_ty = self.context.ptr_type(AddressSpace::default());
927 let cast_ptr = self
928 .builder
929 .build_pointer_cast(var_data_ptr, i64_ptr_ty, "expr_i64_ptr")
930 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
931 self.builder
932 .build_store(cast_ptr, v64)
933 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
934 }
935 n => {
936 let v64 = if value.get_type().get_bit_width() < 64 {
938 self.builder
939 .build_int_s_extend(value, self.context.i64_type(), "expr_sext_fallback")
940 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
941 } else {
942 value
943 };
944 for i in 0..n {
945 let shift = self.context.i64_type().const_int((i * 8) as u64, false);
946 let shifted = self
947 .builder
948 .build_right_shift(v64, shift, false, &format!("expr_shr_{i}"))
949 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
950 let byte = self
951 .builder
952 .build_int_truncate(
953 shifted,
954 self.context.i8_type(),
955 &format!("expr_byte_{i}"),
956 )
957 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
958 let byte_ptr = unsafe {
959 self.builder
960 .build_gep(
961 self.context.i8_type(),
962 var_data_ptr,
963 &[self.context.i32_type().const_int(i as u64, false)],
964 &format!("expr_byte_ptr_{i}"),
965 )
966 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
967 };
968 self.builder
969 .build_store(byte_ptr, byte)
970 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
971 }
972 }
973 }
974
975 Ok(())
977 }
978 fn is_simple_typeinfo(t: &ghostscope_dwarf::TypeInfo) -> bool {
982 use ghostscope_dwarf::TypeInfo as TI;
983 match t {
984 TI::BaseType { size, .. } => matches!(*size, 1 | 2 | 4 | 8),
985 TI::EnumType { base_type, .. } => {
986 let sz = base_type.size();
987 matches!(sz, 1 | 2 | 4 | 8)
988 }
989 TI::PointerType { .. } => true,
990 TI::TypedefType {
991 underlying_type, ..
992 }
993 | TI::QualifiedType {
994 underlying_type, ..
995 } => Self::is_simple_typeinfo(underlying_type),
996 _ => false,
997 }
998 }
999
1000 fn compute_read_size_for_type(t: &ghostscope_dwarf::TypeInfo) -> usize {
1003 use ghostscope_dwarf::TypeInfo as TI;
1004 match t {
1005 TI::ArrayType {
1006 element_type,
1007 element_count,
1008 total_size,
1009 } => {
1010 if let Some(ts) = total_size {
1012 return *ts as usize;
1013 }
1014 let elem_size = element_type.size() as usize;
1016 if elem_size == 0 {
1017 return 0;
1018 }
1019 if let Some(cnt) = element_count {
1020 return elem_size * (*cnt as usize);
1021 }
1022 0
1023 }
1024 TI::TypedefType {
1025 underlying_type, ..
1026 }
1027 | TI::QualifiedType {
1028 underlying_type, ..
1029 } => Self::compute_read_size_for_type(underlying_type),
1030 _ => t.size() as usize,
1031 }
1032 }
1033
1034 fn expr_to_name(&self, expr: &crate::script::ast::Expr) -> String {
1037 use crate::script::ast::Expr as E;
1038 fn inner(e: &E) -> String {
1039 match e {
1040 E::Variable(s) => s.clone(),
1041 E::MemberAccess(obj, field) => format!("{}.{field}", inner(obj)),
1042 E::ArrayAccess(arr, idx) => format!("{}[{}]", inner(arr), inner(idx)),
1043 E::PointerDeref(p) => format!("*{}", inner(p)),
1044 E::AddressOf(p) => format!("&{}", inner(p)),
1045 E::ChainAccess(v) => v.join("."),
1046 E::Int(v) => v.to_string(),
1047 E::String(s) => format!("\"{s}\""),
1048 E::Float(v) => format!("{v}"),
1049 E::UnaryNot(e1) => format!("!{}", inner(e1)),
1050 E::Bool(v) => v.to_string(),
1051 E::SpecialVar(s) => format!("${s}"),
1052 E::BuiltinCall { name, args } => {
1053 let arg_strs: Vec<String> = args.iter().map(inner).collect();
1054 format!("{}({})", name, arg_strs.join(", "))
1055 }
1056 E::BinaryOp { left, op, right } => {
1057 let op_str = match op {
1058 crate::script::ast::BinaryOp::Add => "+",
1059 crate::script::ast::BinaryOp::Subtract => "-",
1060 crate::script::ast::BinaryOp::Multiply => "*",
1061 crate::script::ast::BinaryOp::Divide => "/",
1062 crate::script::ast::BinaryOp::Equal => "==",
1063 crate::script::ast::BinaryOp::NotEqual => "!=",
1064 crate::script::ast::BinaryOp::LessThan => "<",
1065 crate::script::ast::BinaryOp::LessEqual => "<=",
1066 crate::script::ast::BinaryOp::GreaterThan => ">",
1067 crate::script::ast::BinaryOp::GreaterEqual => ">=",
1068 crate::script::ast::BinaryOp::LogicalAnd => "&&",
1069 crate::script::ast::BinaryOp::LogicalOr => "||",
1070 };
1071 format!("({}{}{})", inner(left), op_str, inner(right))
1072 }
1073 }
1074 }
1075 let s_full = inner(expr);
1076 const MAX_NAME: usize = 96;
1077 if s_full.chars().count() > MAX_NAME {
1078 let keep = MAX_NAME.saturating_sub(3);
1080 let mut acc = String::with_capacity(MAX_NAME);
1081 for (i, ch) in s_full.chars().enumerate() {
1082 if i >= keep {
1083 break;
1084 }
1085 acc.push(ch);
1086 }
1087 acc.push_str("...");
1088 acc
1089 } else {
1090 s_full
1091 }
1092 }
1093
1094 fn is_alias_candidate_expr(&mut self, expr: &crate::script::ast::Expr) -> bool {
1101 use crate::script::ast::BinaryOp as BO;
1102 use crate::script::ast::Expr as E;
1103 match expr {
1104 E::Variable(name) if self.alias_variable_exists(name) => true,
1106 E::AddressOf(_) => true,
1108 E::BinaryOp {
1110 left,
1111 op: BO::Add,
1112 right,
1113 } => {
1114 let is_const_nonneg = |e: &E| matches!(e, E::Int(v) if *v >= 0);
1115 (self.is_alias_candidate_expr(left) && is_const_nonneg(right))
1116 || (self.is_alias_candidate_expr(right) && is_const_nonneg(left))
1117 }
1118 other => matches!(self.query_dwarf_for_complex_expr(other), Ok(Some(_))),
1121 }
1122 }
1123
1124 pub fn compile_program_with_staged_transmission(
1128 &mut self,
1129 program: &Program,
1130 _variable_types: HashMap<String, TypeKind>,
1131 ) -> Result<TraceContext> {
1132 info!("Compiling program with staged transmission system");
1133
1134 self.send_trace_event_header()?;
1136 info!("Sent TraceEventHeader");
1137
1138 let trace_id = self.current_trace_id.map(|id| id as u64).unwrap_or(0);
1140 self.send_trace_event_message(trace_id)?;
1141 info!("Sent TraceEventMessage");
1142
1143 self.store_flag_value("_gs_any_fail", 0)?;
1145 self.store_flag_value("_gs_any_success", 0)?;
1146
1147 let mut instruction_count = 0u16;
1149 for statement in &program.statements {
1150 instruction_count += self.compile_statement(statement)?;
1151 }
1152
1153 self.send_end_instruction(instruction_count)?;
1155 info!(
1156 "Sent EndInstruction with {} total instructions",
1157 instruction_count
1158 );
1159
1160 Ok(self.trace_context.clone())
1162 }
1163
1164 pub fn compile_statement(&mut self, statement: &Statement) -> Result<u16> {
1166 debug!("Compiling statement: {:?}", statement);
1167
1168 match statement {
1169 Statement::AliasDeclaration { name, target } => {
1170 info!("Registering alias variable: {} = {:?}", name, target);
1171 self.declare_name_in_current_scope(name)?;
1173 self.set_alias_variable(name, target.clone());
1174 Ok(0)
1175 }
1176 Statement::VarDeclaration { name, value } => {
1177 info!("Processing variable declaration: {} = {:?}", name, value);
1178 self.declare_name_in_current_scope(name)?;
1180 if self.is_alias_candidate_expr(value) {
1182 self.set_alias_variable(name, value.clone());
1183 tracing::debug!(var=%name, "Registered DWARF alias variable");
1184 Ok(0)
1185 } else {
1186 match value {
1189 crate::script::Expr::String(s) => {
1190 let mut bytes = s.as_bytes().to_vec();
1191 bytes.push(0); self.set_string_variable_bytes(name, bytes);
1193 }
1194 crate::script::Expr::Variable(ref nm) => {
1195 if self
1196 .get_variable_type(nm)
1197 .is_some_and(|t| matches!(t, crate::script::VarType::String))
1198 {
1199 if let Some(b) = self.get_string_variable_bytes(nm).cloned() {
1200 self.set_string_variable_bytes(name, b);
1201 }
1202 }
1203 }
1204 _ => {}
1205 }
1206 let compiled_value = self.compile_expr(value)?;
1207 if let BasicValueEnum::PointerValue(_) = compiled_value {
1209 let allow_string_var_copy = match value {
1211 crate::script::Expr::String(_) => true,
1212 crate::script::Expr::Variable(ref nm) => self
1213 .get_variable_type(nm)
1214 .is_some_and(|t| matches!(t, crate::script::VarType::String)),
1215 _ => false,
1216 };
1217 if !allow_string_var_copy {
1218 return Err(CodeGenError::TypeError(
1219 "script variables cannot store pointer values; use DWARF alias (let v = &expr) or keep it as a string".to_string(),
1220 ));
1221 }
1222 }
1223 self.store_variable(name, compiled_value)?;
1224 Ok(0) }
1226 }
1227 Statement::Print(print_stmt) => self.compile_print_statement(print_stmt),
1228 Statement::If {
1229 condition,
1230 then_body,
1231 else_body,
1232 } => {
1233 let expr_text = self.expr_to_name(condition);
1236 let expr_index = self.trace_context.add_string(expr_text);
1237 self.condition_context_active = true;
1239 self.reset_condition_error()?;
1240
1241 let cond_value = self.compile_expr(condition)?;
1243
1244 let cond_bool = match cond_value {
1246 BasicValueEnum::IntValue(int_val) => {
1247 self.builder
1249 .build_int_compare(
1250 inkwell::IntPredicate::NE,
1251 int_val,
1252 int_val.get_type().const_zero(),
1253 "cond_bool",
1254 )
1255 .map_err(|e| {
1256 CodeGenError::LLVMError(format!("Failed to create condition: {e}"))
1257 })?
1258 }
1259 _ => {
1260 return Err(CodeGenError::LLVMError(
1261 "Condition must evaluate to integer".to_string(),
1262 ));
1263 }
1264 };
1265
1266 let current_function = self
1268 .builder
1269 .get_insert_block()
1270 .ok_or_else(|| CodeGenError::LLVMError("No current basic block".to_string()))?
1271 .get_parent()
1272 .ok_or_else(|| CodeGenError::LLVMError("No parent function".to_string()))?;
1273
1274 let then_block = self
1276 .context
1277 .append_basic_block(current_function, "then_block");
1278 let else_block = self
1279 .context
1280 .append_basic_block(current_function, "else_block");
1281 let merge_block = self
1282 .context
1283 .append_basic_block(current_function, "merge_block");
1284 let err_block = self
1285 .context
1286 .append_basic_block(current_function, "cond_err_block");
1287 let ok_block = self
1288 .context
1289 .append_basic_block(current_function, "cond_ok_block");
1290 self.condition_context_active = false;
1292
1293 let cond_err_pred = self.build_condition_error_predicate()?;
1295 self.builder
1296 .build_conditional_branch(cond_err_pred, err_block, ok_block)
1297 .map_err(|e| {
1298 CodeGenError::LLVMError(format!("Failed to branch on cond_err: {e}"))
1299 })?;
1300
1301 self.builder.position_at_end(err_block);
1303 let cond_err_ptr = self.get_or_create_cond_error_global();
1304 let err_code = self
1305 .builder
1306 .build_load(self.context.i8_type(), cond_err_ptr, "cond_err_code")
1307 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
1308 .into_int_value();
1309 let cond_err_addr_ptr = self.get_or_create_cond_error_addr_global();
1311 let err_addr = self
1312 .builder
1313 .build_load(self.context.i64_type(), cond_err_addr_ptr, "cond_err_addr")
1314 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
1315 .into_int_value();
1316 let cond_err_flags_ptr = self.get_or_create_cond_error_flags_global();
1318 let err_flags = self
1319 .builder
1320 .build_load(self.context.i8_type(), cond_err_flags_ptr, "cond_err_flags")
1321 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
1322 .into_int_value();
1323 self.generate_expr_error(expr_index, err_code, err_flags, err_addr)?;
1324 let goto_else = matches!(else_body.as_deref(), Some(Statement::If { .. }));
1327 if goto_else {
1328 self.builder
1329 .build_unconditional_branch(else_block)
1330 .map_err(|e| {
1331 CodeGenError::LLVMError(format!(
1332 "Failed to branch to else on error: {e}"
1333 ))
1334 })?;
1335 } else {
1336 self.builder
1337 .build_unconditional_branch(merge_block)
1338 .map_err(|e| {
1339 CodeGenError::LLVMError(format!(
1340 "Failed to branch to merge on error: {e}"
1341 ))
1342 })?;
1343 }
1344
1345 self.builder.position_at_end(ok_block);
1347 self.builder
1348 .build_conditional_branch(cond_bool, then_block, else_block)
1349 .map_err(|e| {
1350 CodeGenError::LLVMError(format!("Failed to create branch: {e}"))
1351 })?;
1352
1353 self.builder.position_at_end(then_block);
1355 let mut then_instructions = 0u16;
1356 self.enter_scope();
1357 for stmt in then_body {
1358 then_instructions += self.compile_statement(stmt)?;
1359 }
1360 self.exit_scope();
1361 self.builder
1362 .build_unconditional_branch(merge_block)
1363 .map_err(|e| {
1364 CodeGenError::LLVMError(format!("Failed to branch to merge: {e}"))
1365 })?;
1366
1367 self.builder.position_at_end(else_block);
1369 let mut else_instructions = 0u16;
1370 if let Some(else_stmt) = else_body {
1371 self.enter_scope();
1372 else_instructions += self.compile_statement(else_stmt)?;
1373 self.exit_scope();
1374 }
1375 self.builder
1376 .build_unconditional_branch(merge_block)
1377 .map_err(|e| {
1378 CodeGenError::LLVMError(format!("Failed to branch to merge: {e}"))
1379 })?;
1380
1381 self.builder.position_at_end(merge_block);
1383
1384 Ok(std::cmp::max(then_instructions, else_instructions))
1386 }
1387 Statement::Block(nested_statements) => {
1388 let mut total_instructions = 0u16;
1389 self.enter_scope();
1390 for stmt in nested_statements {
1391 total_instructions += self.compile_statement(stmt)?;
1392 }
1393 self.exit_scope();
1394 Ok(total_instructions)
1395 }
1396 Statement::TracePoint { pattern: _, body } => {
1397 let mut total_instructions = 0u16;
1398 self.enter_scope();
1400 for stmt in body {
1401 total_instructions += self.compile_statement(stmt)?;
1402 }
1403 self.exit_scope();
1404 Ok(total_instructions)
1405 }
1406 _ => {
1407 warn!("Unsupported statement type: {:?}", statement);
1408 Ok(0)
1409 }
1410 }
1411 }
1412
1413 pub fn compile_print_statement(&mut self, print_stmt: &PrintStatement) -> Result<u16> {
1415 info!("Compiling print statement: {:?}", print_stmt);
1416
1417 match print_stmt {
1418 PrintStatement::String(s) => {
1419 info!("Processing string literal: {}", s);
1420 let string_index = self.trace_context.add_string(s.to_string());
1422 self.generate_print_string_index(string_index)?;
1424 Ok(1) }
1426 PrintStatement::Variable(var_name) => {
1427 info!("Processing variable: {}", var_name);
1428 let expr = crate::script::Expr::Variable(var_name.clone());
1429 let arg = self.resolve_expr_to_arg(&expr)?;
1430 let n = self.emit_print_from_arg(arg)?;
1431 tracing::trace!(
1432 var_name = %var_name,
1433 instructions = n,
1434 "compile_print_statement: emitted via unified resolver"
1435 );
1436 Ok(n)
1437 }
1438 PrintStatement::ComplexVariable(expr) => {
1439 info!("Processing complex variable: {:?}", expr);
1440 let arg = self.resolve_expr_to_arg(expr)?;
1441 let n = self.emit_print_from_arg(arg)?;
1442 tracing::trace!(
1443 instructions = n,
1444 "compile_print_statement: emitted via unified resolver"
1445 );
1446 Ok(n)
1447 }
1448 PrintStatement::Formatted { format, args } => {
1449 info!(
1450 "Processing formatted print: '{}' with {} args",
1451 format,
1452 args.len()
1453 );
1454 self.compile_formatted_print(format, args)
1455 }
1456 }
1457 }
1458
1459 fn compile_formatted_print(
1461 &mut self,
1462 format: &str,
1463 args: &[crate::script::ast::Expr],
1464 ) -> Result<u16> {
1465 info!(
1466 "Compiling formatted print: '{}' with {} arguments",
1467 format,
1468 args.len()
1469 );
1470 let format_string_index = self.trace_context.add_string(format.to_string());
1471 let mut complex_args: Vec<ComplexArg<'ctx>> = Vec::with_capacity(args.len());
1472
1473 #[derive(Clone, Copy, Debug, PartialEq)]
1475 enum Conv {
1476 Default,
1477 HexLower,
1478 HexUpper,
1479 Ptr,
1480 Ascii,
1481 }
1482 #[derive(Clone, Debug, PartialEq)]
1483 enum LenSpec {
1484 None,
1485 Static(usize),
1486 Star,
1487 Capture(String),
1488 }
1489
1490 fn parse_slots(fmt: &str) -> Vec<(Conv, LenSpec)> {
1491 let mut res = Vec::new();
1492 let mut it = fmt.chars().peekable();
1493 while let Some(ch) = it.next() {
1494 if ch == '{' {
1495 if it.peek() == Some(&'{') {
1496 it.next();
1497 continue;
1498 }
1499 let mut content = String::new();
1500 for c in it.by_ref() {
1501 if c == '}' {
1502 break;
1503 }
1504 content.push(c);
1505 }
1506 if content.is_empty() {
1507 res.push((Conv::Default, LenSpec::None));
1508 } else if let Some(rest) = content.strip_prefix(':') {
1509 let mut sit = rest.chars();
1510 let conv = match sit.next().unwrap_or(' ') {
1511 'x' => Conv::HexLower,
1512 'X' => Conv::HexUpper,
1513 'p' => Conv::Ptr,
1514 's' => Conv::Ascii,
1515 _ => Conv::Default,
1516 };
1517 let rest: String = sit.collect();
1518 let lens = if rest.is_empty() {
1519 LenSpec::None
1520 } else if let Some(r) = rest.strip_prefix('.') {
1521 if r == "*" {
1522 LenSpec::Star
1523 } else if let Some(s) = r.strip_suffix('$') {
1524 LenSpec::Capture(s.to_string())
1525 } else if r.chars().all(|c| c.is_ascii_digit()) {
1526 LenSpec::Static(r.parse::<usize>().unwrap_or(0))
1527 } else {
1528 LenSpec::None
1529 }
1530 } else {
1531 LenSpec::None
1532 };
1533 res.push((conv, lens));
1534 } else {
1535 res.push((Conv::Default, LenSpec::None));
1536 }
1537 }
1538 }
1539 res
1540 }
1541
1542 let slots = parse_slots(format);
1543 let mut ai = 0usize; for (conv, lens) in slots.into_iter() {
1545 match conv {
1546 Conv::Default => {
1547 if ai >= args.len() {
1548 break;
1549 }
1550 let a = self.resolve_expr_to_arg(&args[ai])?;
1551 complex_args.push(a);
1552 ai += 1;
1553 }
1554 Conv::Ptr => {
1555 if ai >= args.len() {
1556 break;
1557 }
1558 let expr = &args[ai];
1560 let val = self.compile_expr(expr)?;
1562 let iv = match val {
1563 BasicValueEnum::IntValue(iv) => iv,
1564 BasicValueEnum::PointerValue(pv) => self
1565 .builder
1566 .build_ptr_to_int(pv, self.context.i64_type(), "ptr_to_i64")
1567 .map_err(|e| CodeGenError::Builder(e.to_string()))?,
1568 _ => self
1569 .compile_dwarf_expression(expr)
1570 .and_then(|bv| match bv {
1571 BasicValueEnum::IntValue(iv) => Ok(iv),
1572 BasicValueEnum::PointerValue(pv) => self
1573 .builder
1574 .build_ptr_to_int(pv, self.context.i64_type(), "ptr_to_i64")
1575 .map_err(|e| CodeGenError::Builder(e.to_string())),
1576 _ => Err(CodeGenError::TypeError("pointer expected".into())),
1577 })?,
1578 };
1579 complex_args.push(ComplexArg {
1580 var_name_index: self
1581 .trace_context
1582 .add_variable_name(self.expr_to_name(expr)),
1583 type_index: self.add_synthesized_type_index_for_kind(TypeKind::Pointer),
1584 access_path: Vec::new(),
1585 data_len: 8,
1586 source: ComplexArgSource::ComputedInt {
1587 value: iv,
1588 byte_len: 8,
1589 },
1590 });
1591 ai += 1;
1592 }
1593 Conv::HexLower | Conv::HexUpper | Conv::Ascii => {
1594 let wants_ascii = matches!(conv, Conv::Ascii);
1597 match lens {
1598 LenSpec::Static(n) if ai < args.len() => {
1599 let expr = &args[ai];
1601 let val = self.compile_expr(expr).ok();
1603 let mut addr_iv: Option<IntValue> = match val {
1604 Some(BasicValueEnum::PointerValue(pv)) => Some(
1605 self.builder
1606 .build_ptr_to_int(pv, self.context.i64_type(), "ptr_to_i64")
1607 .map_err(|e| CodeGenError::Builder(e.to_string()))?,
1608 ),
1609 _ => None,
1610 };
1611 if addr_iv.is_none() {
1613 if let Some(BasicValueEnum::IntValue(iv)) = val {
1614 if let Some(var) = self.query_dwarf_for_complex_expr(expr)? {
1615 if let Some(ref t) = var.dwarf_type {
1616 if matches!(
1617 t,
1618 ghostscope_dwarf::TypeInfo::PointerType { .. }
1619 ) {
1620 addr_iv = Some(iv);
1621 }
1622 }
1623 }
1624 }
1625 }
1626 let addr_iv = if let Some(iv) = addr_iv {
1627 iv
1628 } else {
1629 let var =
1631 self.query_dwarf_for_complex_expr(expr)?.ok_or_else(|| {
1632 CodeGenError::VariableNotFound(format!("{expr:?}"))
1633 })?;
1634 let mod_hint = self.take_module_hint();
1635 self.evaluation_result_to_address_with_hint(
1636 &var.evaluation_result,
1637 None,
1638 mod_hint.as_deref(),
1639 )?
1640 };
1641 complex_args.push(ComplexArg {
1642 var_name_index: self
1643 .trace_context
1644 .add_variable_name(self.expr_to_name(expr)),
1645 type_index: self
1646 .trace_context
1647 .add_type(ghostscope_dwarf::TypeInfo::ArrayType {
1648 element_type: Box::new(ghostscope_dwarf::TypeInfo::BaseType {
1649 name: "u8".into(),
1650 size: 1,
1651 encoding: ghostscope_dwarf::constants::DW_ATE_unsigned_char
1652 .0
1653 as u16,
1654 }),
1655 element_count: Some(n as u64),
1656 total_size: Some(n as u64),
1657 }),
1658 access_path: Vec::new(),
1659 data_len: n,
1660 source: ComplexArgSource::MemDump {
1661 src_addr: addr_iv,
1662 len: n,
1663 },
1664 });
1665 ai += 1;
1666 }
1667 LenSpec::Star => {
1668 if ai + 1 >= args.len() {
1670 break;
1671 }
1672 let len_expr = &args[ai];
1674 let len_val = self.compile_expr(len_expr)?;
1675 let (len_iv, byte_len) = match len_val {
1676 BasicValueEnum::IntValue(iv) => (iv, 8usize),
1677 _ => {
1678 return Err(CodeGenError::TypeError(
1679 "length must be integer".into(),
1680 ))
1681 }
1682 };
1683 complex_args.push(ComplexArg {
1684 var_name_index: self
1685 .trace_context
1686 .add_variable_name("__len".into()),
1687 type_index: self.add_synthesized_type_index_for_kind(TypeKind::U64),
1688 access_path: Vec::new(),
1689 data_len: byte_len,
1690 source: ComplexArgSource::ComputedInt {
1691 value: len_iv,
1692 byte_len,
1693 },
1694 });
1695
1696 let val_expr = &args[ai + 1];
1698 let val = self.compile_expr(val_expr).ok();
1700 let mut addr_iv: Option<IntValue> = match val {
1701 Some(BasicValueEnum::PointerValue(pv)) => Some(
1702 self.builder
1703 .build_ptr_to_int(pv, self.context.i64_type(), "ptr_to_i64")
1704 .map_err(|e| CodeGenError::Builder(e.to_string()))?,
1705 ),
1706 _ => None,
1707 };
1708 if addr_iv.is_none() {
1709 if let Some(BasicValueEnum::IntValue(iv)) = val {
1710 if let Some(var) =
1711 self.query_dwarf_for_complex_expr(val_expr)?
1712 {
1713 if let Some(ref t) = var.dwarf_type {
1714 if matches!(
1715 t,
1716 ghostscope_dwarf::TypeInfo::PointerType { .. }
1717 ) {
1718 addr_iv = Some(iv);
1719 }
1720 }
1721 }
1722 }
1723 }
1724 let addr_iv = if let Some(iv) = addr_iv {
1725 iv
1726 } else {
1727 let var = self.query_dwarf_for_complex_expr(val_expr)?.ok_or_else(
1728 || CodeGenError::VariableNotFound(format!("{val_expr:?}")),
1729 )?;
1730 let mod_hint = self.take_module_hint();
1731 self.evaluation_result_to_address_with_hint(
1732 &var.evaluation_result,
1733 None,
1734 mod_hint.as_deref(),
1735 )?
1736 };
1737 let cap = self.compile_options.mem_dump_cap as usize;
1739 complex_args.push(ComplexArg {
1740 var_name_index: self
1741 .trace_context
1742 .add_variable_name(self.expr_to_name(val_expr)),
1743 type_index: self
1744 .trace_context
1745 .add_type(ghostscope_dwarf::TypeInfo::ArrayType {
1746 element_type: Box::new(ghostscope_dwarf::TypeInfo::BaseType {
1747 name: "u8".into(),
1748 size: 1,
1749 encoding: ghostscope_dwarf::constants::DW_ATE_unsigned_char
1750 .0
1751 as u16,
1752 }),
1753 element_count: Some(cap as u64),
1754 total_size: Some(cap as u64),
1755 }),
1756 access_path: Vec::new(),
1757 data_len: cap,
1758 source: ComplexArgSource::MemDumpDynamic {
1759 src_addr: addr_iv,
1760 len_value: len_iv,
1761 max_len: cap,
1762 },
1763 });
1764 ai += 2;
1765 }
1766 LenSpec::Capture(name) => {
1767 if ai >= args.len() {
1769 break;
1770 }
1771 if !self.variable_exists(&name) {
1772 return Err(CodeGenError::TypeError(format!(
1773 "capture length variable '{name}' not found"
1774 )));
1775 }
1776 let len_val = self.load_variable(&name)?;
1778 let (len_iv, byte_len) = match len_val {
1779 BasicValueEnum::IntValue(iv) => (iv, 8usize),
1780 BasicValueEnum::PointerValue(pv) => (
1781 self.builder
1782 .build_ptr_to_int(
1783 pv,
1784 self.context.i64_type(),
1785 "len_ptr_to_i64",
1786 )
1787 .map_err(|e| CodeGenError::Builder(e.to_string()))?,
1788 8usize,
1789 ),
1790 _ => {
1791 return Err(CodeGenError::TypeError(
1792 "length must be integer/pointer".into(),
1793 ))
1794 }
1795 };
1796 complex_args.push(ComplexArg {
1797 var_name_index: self.trace_context.add_variable_name(name.clone()),
1798 type_index: self.add_synthesized_type_index_for_kind(TypeKind::U64),
1799 access_path: Vec::new(),
1800 data_len: byte_len,
1801 source: ComplexArgSource::ComputedInt {
1802 value: len_iv,
1803 byte_len,
1804 },
1805 });
1806
1807 let val_expr = &args[ai];
1809 let val = self.compile_expr(val_expr).ok();
1810 let mut addr_iv: Option<IntValue> = match val {
1811 Some(BasicValueEnum::PointerValue(pv)) => Some(
1812 self.builder
1813 .build_ptr_to_int(pv, self.context.i64_type(), "ptr_to_i64")
1814 .map_err(|e| CodeGenError::Builder(e.to_string()))?,
1815 ),
1816 _ => None,
1817 };
1818 if addr_iv.is_none() {
1819 if let Some(BasicValueEnum::IntValue(iv)) = val {
1820 if let Some(var) =
1821 self.query_dwarf_for_complex_expr(val_expr)?
1822 {
1823 if let Some(ref t) = var.dwarf_type {
1824 if matches!(
1825 t,
1826 ghostscope_dwarf::TypeInfo::PointerType { .. }
1827 ) {
1828 addr_iv = Some(iv);
1829 }
1830 }
1831 }
1832 }
1833 }
1834 let addr_iv = if let Some(iv) = addr_iv {
1835 iv
1836 } else {
1837 let var = self.query_dwarf_for_complex_expr(val_expr)?.ok_or_else(
1838 || CodeGenError::VariableNotFound(format!("{val_expr:?}")),
1839 )?;
1840 let mod_hint = self.take_module_hint();
1841 self.evaluation_result_to_address_with_hint(
1842 &var.evaluation_result,
1843 None,
1844 mod_hint.as_deref(),
1845 )?
1846 };
1847 let cap = self.compile_options.mem_dump_cap as usize;
1848 complex_args.push(ComplexArg {
1849 var_name_index: self
1850 .trace_context
1851 .add_variable_name(self.expr_to_name(val_expr)),
1852 type_index: self
1853 .trace_context
1854 .add_type(ghostscope_dwarf::TypeInfo::ArrayType {
1855 element_type: Box::new(ghostscope_dwarf::TypeInfo::BaseType {
1856 name: "u8".into(),
1857 size: 1,
1858 encoding: ghostscope_dwarf::constants::DW_ATE_unsigned_char
1859 .0
1860 as u16,
1861 }),
1862 element_count: Some(cap as u64),
1863 total_size: Some(cap as u64),
1864 }),
1865 access_path: Vec::new(),
1866 data_len: cap,
1867 source: ComplexArgSource::MemDumpDynamic {
1868 src_addr: addr_iv,
1869 len_value: len_iv,
1870 max_len: cap,
1871 },
1872 });
1873 ai += 1;
1874 }
1875 _ => {
1876 if ai >= args.len() {
1878 break;
1879 }
1880 complex_args.push(self.resolve_expr_to_arg(&args[ai])?);
1881 ai += 1;
1882 }
1883 }
1884 let _ = wants_ascii; }
1886 }
1887 }
1888 self.generate_print_complex_format_instruction(format_string_index, &complex_args)?;
1889 Ok(1)
1890 }
1891
1892 pub fn resolve_variable_with_priority(&mut self, var_name: &str) -> Result<(u16, TypeKind)> {
1895 info!("Resolving variable '{}' with correct priority", var_name);
1896
1897 if self.variable_exists(var_name) {
1899 info!("Found script variable: {}", var_name);
1900
1901 let loaded_value = self.load_variable(var_name)?;
1903 let type_encoding = self.infer_type_from_llvm_value(&loaded_value);
1904
1905 let var_name_index = self.trace_context.add_variable_name(var_name.to_string());
1907
1908 return Ok((var_name_index, type_encoding));
1909 }
1910
1911 info!(
1913 "Variable '{}' not found in script variables, checking DWARF",
1914 var_name
1915 );
1916
1917 let compile_context = self.get_compile_time_context()?.clone();
1918 let variable_with_eval = match self.query_dwarf_for_variable(var_name)? {
1919 Some(var) => var,
1920 None => {
1921 return Err(CodeGenError::VariableNotFound(format!(
1922 "Variable '{}' not found in script or DWARF at PC 0x{:x} in module '{}'",
1923 var_name, compile_context.pc_address, compile_context.module_path
1924 )));
1925 }
1926 };
1927
1928 let dwarf_type = variable_with_eval.dwarf_type.as_ref().ok_or_else(|| {
1930 CodeGenError::DwarfError("Variable has no DWARF type information".to_string())
1931 })?;
1932 let type_encoding = TypeKind::from(dwarf_type);
1933
1934 let var_name_index = self.trace_context.add_variable_name(var_name.to_string());
1936
1937 info!(
1938 "DWARF variable '{}' resolved successfully with type: {:?}",
1939 var_name, type_encoding
1940 );
1941
1942 Ok((var_name_index, type_encoding))
1943 }
1944
1945 fn synthesize_typeinfo_for_typekind(&self, kind: TypeKind) -> ghostscope_dwarf::TypeInfo {
1947 use ghostscope_dwarf::constants::{
1948 DW_ATE_boolean, DW_ATE_float, DW_ATE_signed, DW_ATE_signed_char, DW_ATE_unsigned,
1949 };
1950 use ghostscope_dwarf::TypeInfo as TI;
1951
1952 match kind {
1953 TypeKind::Bool => TI::BaseType {
1954 name: "bool".to_string(),
1955 size: 1,
1956 encoding: DW_ATE_boolean.0 as u16,
1957 },
1958 TypeKind::F32 => TI::BaseType {
1959 name: "f32".to_string(),
1960 size: 4,
1961 encoding: DW_ATE_float.0 as u16,
1962 },
1963 TypeKind::F64 => TI::BaseType {
1964 name: "f64".to_string(),
1965 size: 8,
1966 encoding: DW_ATE_float.0 as u16,
1967 },
1968 TypeKind::I8 => TI::BaseType {
1969 name: "i8".to_string(),
1970 size: 1,
1971 encoding: DW_ATE_signed_char.0 as u16,
1972 },
1973 TypeKind::I16 => TI::BaseType {
1974 name: "i16".to_string(),
1975 size: 2,
1976 encoding: DW_ATE_signed.0 as u16,
1977 },
1978 TypeKind::I32 => TI::BaseType {
1979 name: "i32".to_string(),
1980 size: 4,
1981 encoding: DW_ATE_signed.0 as u16,
1982 },
1983 TypeKind::I64 => TI::BaseType {
1984 name: "i64".to_string(),
1985 size: 8,
1986 encoding: DW_ATE_signed.0 as u16,
1987 },
1988 TypeKind::U8 | TypeKind::Char => TI::BaseType {
1989 name: "u8".to_string(),
1990 size: 1,
1991 encoding: DW_ATE_unsigned.0 as u16,
1992 },
1993 TypeKind::U16 => TI::BaseType {
1994 name: "u16".to_string(),
1995 size: 2,
1996 encoding: DW_ATE_unsigned.0 as u16,
1997 },
1998 TypeKind::U32 => TI::BaseType {
1999 name: "u32".to_string(),
2000 size: 4,
2001 encoding: DW_ATE_unsigned.0 as u16,
2002 },
2003 TypeKind::U64 => TI::BaseType {
2004 name: "u64".to_string(),
2005 size: 8,
2006 encoding: DW_ATE_unsigned.0 as u16,
2007 },
2008 TypeKind::Pointer | TypeKind::CString | TypeKind::String | TypeKind::Unknown => {
2009 TI::PointerType {
2011 target_type: Box::new(TI::UnknownType {
2012 name: "void".to_string(),
2013 }),
2014 size: 8,
2015 }
2016 }
2017 TypeKind::NullPointer => TI::PointerType {
2018 target_type: Box::new(TI::UnknownType {
2019 name: "void".to_string(),
2020 }),
2021 size: 8,
2022 },
2023 _ => TI::BaseType {
2024 name: "i64".to_string(),
2025 size: 8,
2026 encoding: DW_ATE_signed.0 as u16,
2027 },
2028 }
2029 }
2030
2031 fn add_synthesized_type_index_for_kind(&mut self, kind: TypeKind) -> u16 {
2032 let ti = self.synthesize_typeinfo_for_typekind(kind);
2033 self.trace_context.add_type(ti)
2034 }
2035
2036 fn infer_type_from_llvm_value(&self, value: &BasicValueEnum<'_>) -> TypeKind {
2039 match value {
2040 BasicValueEnum::IntValue(int_val) => {
2041 match int_val.get_type().get_bit_width() {
2042 1 => TypeKind::Bool,
2043 8 => TypeKind::I8, 16 => TypeKind::I16,
2045 32 => TypeKind::I32,
2046 64 => TypeKind::I64,
2047 _ => TypeKind::I64, }
2049 }
2050 BasicValueEnum::FloatValue(float_val) => {
2051 match float_val.get_type() {
2052 t if t == self.context.f32_type() => TypeKind::F32,
2053 t if t == self.context.f64_type() => TypeKind::F64,
2054 _ => TypeKind::F64, }
2056 }
2057 BasicValueEnum::PointerValue(_) => TypeKind::Pointer,
2058 _ => TypeKind::I64, }
2060 }
2061
2062 fn generate_print_complex_format_instruction(
2064 &mut self,
2065 format_string_index: u16,
2066 complex_args: &[ComplexArg<'ctx>],
2067 ) -> Result<()> {
2068 use ghostscope_protocol::trace_event::PrintComplexFormatData;
2069 use InstructionType::PrintComplexFormat as IT;
2070
2071 const INSTR_BUF_CAP: usize = 4096;
2074 let fixed_overhead = std::mem::size_of::<InstructionHeader>()
2075 + std::mem::size_of::<PrintComplexFormatData>();
2076
2077 let mut arg_count = 0u8;
2079 let mut headers_total = 0usize;
2080 let mut static_payload_total = 0usize;
2081 let mut dynamic_indices: Vec<(usize, usize)> = Vec::new(); let mut header_lens: Vec<usize> = Vec::with_capacity(complex_args.len());
2083 for (idx, a) in complex_args.iter().enumerate() {
2084 let header_len = 2 + 2 + 1 + 1 + 2 + a.access_path.len();
2086 header_lens.push(header_len);
2087 headers_total += header_len;
2088
2089 match &a.source {
2090 ComplexArgSource::ImmediateBytes { bytes } => static_payload_total += bytes.len(),
2091 ComplexArgSource::AddressValue { .. } => static_payload_total += 8,
2092 ComplexArgSource::RuntimeRead { .. } => {
2093 static_payload_total += std::cmp::max(a.data_len, 12)
2094 }
2095 ComplexArgSource::ComputedInt { byte_len, .. } => static_payload_total += *byte_len,
2096 ComplexArgSource::MemDump { len, .. } => {
2097 static_payload_total += std::cmp::max(*len, 12)
2098 }
2099 ComplexArgSource::MemDumpDynamic { max_len, .. } => {
2100 dynamic_indices.push((idx, *max_len))
2101 }
2102 }
2103 arg_count = arg_count.saturating_add(1);
2104 }
2105
2106 let mut remaining_for_payload = INSTR_BUF_CAP
2109 .saturating_sub(fixed_overhead)
2110 .saturating_sub(headers_total);
2111
2112 remaining_for_payload = remaining_for_payload.saturating_sub(static_payload_total);
2114
2115 let mut effective_reserved: Vec<usize> = Vec::with_capacity(complex_args.len());
2118 for (idx, a) in complex_args.iter().enumerate() {
2119 let reserved = match &a.source {
2120 ComplexArgSource::ImmediateBytes { bytes } => bytes.len(),
2121 ComplexArgSource::AddressValue { .. } => 8,
2122 ComplexArgSource::RuntimeRead { .. } => std::cmp::max(a.data_len, 12),
2123 ComplexArgSource::ComputedInt { byte_len, .. } => *byte_len,
2124 ComplexArgSource::MemDump { len, .. } => std::cmp::max(*len, 12),
2125 ComplexArgSource::MemDumpDynamic { .. } => {
2126 let (_, max_len) = dynamic_indices
2128 .iter()
2129 .copied()
2130 .find(|(i, _)| *i == idx)
2131 .unwrap_or((idx, 0));
2132 let need = std::cmp::max(12usize, max_len);
2134 let eff = std::cmp::min(need, remaining_for_payload);
2135 remaining_for_payload = remaining_for_payload.saturating_sub(eff);
2136 eff
2137 }
2138 };
2139 effective_reserved.push(reserved);
2140 }
2141
2142 let total_args_payload: usize =
2144 header_lens.iter().sum::<usize>() + effective_reserved.iter().sum::<usize>();
2145 let inst_data_size = std::mem::size_of::<PrintComplexFormatData>() + total_args_payload;
2146 let total_size = std::mem::size_of::<InstructionHeader>() + inst_data_size;
2147
2148 let buffer = self.reserve_instruction_region(total_size as u64);
2150
2151 let inst_type_val = self.context.i8_type().const_int(IT as u8 as u64, false);
2155 self.builder
2156 .build_store(buffer, inst_type_val)
2157 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store inst_type: {e}")))?;
2158 let data_length_ptr = unsafe {
2160 self.builder
2161 .build_gep(
2162 self.context.i8_type(),
2163 buffer,
2164 &[self.context.i32_type().const_int(1, false)],
2165 "data_length_ptr",
2166 )
2167 .map_err(|e| {
2168 CodeGenError::LLVMError(format!("Failed to get data_length GEP: {e}"))
2169 })?
2170 };
2171 let data_length_i16_ptr = self
2172 .builder
2173 .build_pointer_cast(
2174 data_length_ptr,
2175 self.context.ptr_type(AddressSpace::default()),
2176 "data_length_i16_ptr",
2177 )
2178 .map_err(|e| CodeGenError::LLVMError(format!("Failed to cast data_length ptr: {e}")))?;
2179 let data_length_val = self
2180 .context
2181 .i16_type()
2182 .const_int(inst_data_size as u64, false);
2183 self.builder
2184 .build_store(data_length_i16_ptr, data_length_val)
2185 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store data_length: {e}")))?;
2186
2187 let data_ptr = unsafe {
2189 self.builder
2190 .build_gep(
2191 self.context.i8_type(),
2192 buffer,
2193 &[self.context.i32_type().const_int(4, false)],
2194 "pcf_data_ptr",
2195 )
2196 .map_err(|e| {
2197 CodeGenError::LLVMError(format!("Failed to get pcf_data_ptr GEP: {e}"))
2198 })?
2199 };
2200
2201 let fsi_ptr = self
2203 .builder
2204 .build_pointer_cast(
2205 data_ptr,
2206 self.context.ptr_type(AddressSpace::default()),
2207 "fsi_ptr",
2208 )
2209 .map_err(|e| CodeGenError::LLVMError(format!("Failed to cast fsi_ptr: {e}")))?;
2210 let fsi_val = self
2211 .context
2212 .i16_type()
2213 .const_int(format_string_index as u64, false);
2214 self.builder
2215 .build_store(fsi_ptr, fsi_val)
2216 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store fsi: {e}")))?;
2217 let arg_cnt_ptr = unsafe {
2219 self.builder
2220 .build_gep(
2221 self.context.i8_type(),
2222 data_ptr,
2223 &[self.context.i32_type().const_int(2, false)],
2224 "arg_count_ptr",
2225 )
2226 .map_err(|e| CodeGenError::LLVMError(format!("Failed to get arg_count GEP: {e}")))?
2227 };
2228 self.builder
2229 .build_store(
2230 arg_cnt_ptr,
2231 self.context.i8_type().const_int(arg_count as u64, false),
2232 )
2233 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store arg_count: {e}")))?;
2234
2235 let mut offset = std::mem::size_of::<PrintComplexFormatData>();
2237 for (arg_index, a) in complex_args.iter().enumerate() {
2238 let reserved_len = effective_reserved[arg_index];
2240
2241 let arg_base = unsafe {
2243 self.builder
2244 .build_gep(
2245 self.context.i8_type(),
2246 data_ptr,
2247 &[self.context.i32_type().const_int(offset as u64, false)],
2248 "arg_base",
2249 )
2250 .map_err(|e| {
2251 CodeGenError::LLVMError(format!("Failed to get arg_base GEP: {e}"))
2252 })?
2253 };
2254
2255 let vni_cast = self
2257 .builder
2258 .build_pointer_cast(
2259 arg_base,
2260 self.context.ptr_type(AddressSpace::default()),
2261 "vni_cast",
2262 )
2263 .map_err(|e| CodeGenError::LLVMError(format!("Failed to cast vni ptr: {e}")))?;
2264 self.builder
2265 .build_store(
2266 vni_cast,
2267 self.context
2268 .i16_type()
2269 .const_int(a.var_name_index as u64, false),
2270 )
2271 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store vni: {e}")))?;
2272
2273 let ti_ptr = unsafe {
2275 self.builder
2276 .build_gep(
2277 self.context.i8_type(),
2278 arg_base,
2279 &[self.context.i32_type().const_int(2, false)],
2280 "ti_ptr",
2281 )
2282 .map_err(|e| CodeGenError::LLVMError(format!("Failed to get ti GEP: {e}")))?
2283 };
2284 let ti_cast = self
2285 .builder
2286 .build_pointer_cast(
2287 ti_ptr,
2288 self.context.ptr_type(AddressSpace::default()),
2289 "ti_cast",
2290 )
2291 .map_err(|e| CodeGenError::LLVMError(format!("Failed to cast ti ptr: {e}")))?;
2292 self.builder
2293 .build_store(
2294 ti_cast,
2295 self.context
2296 .i16_type()
2297 .const_int(a.type_index as u64, false),
2298 )
2299 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store ti: {e}")))?;
2300
2301 let apl_ptr = unsafe {
2303 self.builder
2304 .build_gep(
2305 self.context.i8_type(),
2306 arg_base,
2307 &[self.context.i32_type().const_int(5, false)],
2308 "status_ptr",
2309 )
2310 .map_err(|e| {
2311 CodeGenError::LLVMError(format!("Failed to get status GEP: {e}"))
2312 })?
2313 };
2314 self.builder
2315 .build_store(apl_ptr, self.context.i8_type().const_int(0, false))
2316 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store status: {e}")))?;
2317
2318 let apl_ptr2 = unsafe {
2320 self.builder
2321 .build_gep(
2322 self.context.i8_type(),
2323 arg_base,
2324 &[self.context.i32_type().const_int(4, false)],
2325 "apl_ptr",
2326 )
2327 .map_err(|e| CodeGenError::LLVMError(format!("Failed to get apl GEP: {e}")))?
2328 };
2329 self.builder
2330 .build_store(
2331 apl_ptr2,
2332 self.context
2333 .i8_type()
2334 .const_int(a.access_path.len() as u64, false),
2335 )
2336 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store apl: {e}")))?;
2337
2338 for (i, b) in a.access_path.iter().enumerate() {
2340 let byte_ptr = unsafe {
2341 self.builder
2342 .build_gep(
2343 self.context.i8_type(),
2344 arg_base,
2345 &[self.context.i32_type().const_int((6 + i) as u64, false)],
2346 &format!("ap_byte_{i}"),
2347 )
2348 .map_err(|e| {
2349 CodeGenError::LLVMError(format!("Failed to get ap byte GEP: {e}"))
2350 })?
2351 };
2352 self.builder
2353 .build_store(byte_ptr, self.context.i8_type().const_int(*b as u64, false))
2354 .map_err(|e| {
2355 CodeGenError::LLVMError(format!("Failed to store ap byte: {e}"))
2356 })?;
2357 }
2358
2359 let dl_ptr = unsafe {
2361 self.builder
2362 .build_gep(
2363 self.context.i8_type(),
2364 arg_base,
2365 &[self
2366 .context
2367 .i32_type()
2368 .const_int((6 + a.access_path.len()) as u64, false)],
2369 "dl_ptr",
2370 )
2371 .map_err(|e| CodeGenError::LLVMError(format!("Failed to get dl GEP: {e}")))?
2372 };
2373 let dl_cast = self
2374 .builder
2375 .build_pointer_cast(
2376 dl_ptr,
2377 self.context.ptr_type(AddressSpace::default()),
2378 "dl_cast",
2379 )
2380 .map_err(|e| CodeGenError::LLVMError(format!("Failed to cast dl ptr: {e}")))?;
2381 self.builder
2382 .build_store(
2383 dl_cast,
2384 self.context
2385 .i16_type()
2386 .const_int(reserved_len as u64, false),
2387 )
2388 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store data_len: {e}")))?;
2389
2390 let var_data_ptr = unsafe {
2392 self.builder
2393 .build_gep(
2394 self.context.i8_type(),
2395 arg_base,
2396 &[self
2397 .context
2398 .i32_type()
2399 .const_int((8 + a.access_path.len()) as u64, false)],
2400 "var_data_ptr",
2401 )
2402 .map_err(|e| {
2403 CodeGenError::LLVMError(format!("Failed to get var_data GEP: {e}"))
2404 })?
2405 };
2406
2407 match &a.source {
2410 ComplexArgSource::ImmediateBytes { bytes, .. } => {
2411 for (i, b) in bytes.iter().enumerate() {
2412 let byte_ptr = unsafe {
2413 self.builder
2414 .build_gep(
2415 self.context.i8_type(),
2416 var_data_ptr,
2417 &[self.context.i32_type().const_int(i as u64, false)],
2418 &format!("var_byte_{i}"),
2419 )
2420 .map_err(|e| {
2421 CodeGenError::LLVMError(format!(
2422 "Failed to get var byte GEP: {e}"
2423 ))
2424 })?
2425 };
2426 self.builder
2427 .build_store(
2428 byte_ptr,
2429 self.context.i8_type().const_int(*b as u64, false),
2430 )
2431 .map_err(|e| {
2432 CodeGenError::LLVMError(format!("Failed to store var byte: {e}"))
2433 })?;
2434 }
2435 }
2437 ComplexArgSource::MemDump { src_addr, len } => {
2438 let ptr_ty = self.context.ptr_type(AddressSpace::default());
2440 let i64_ty = self.context.i64_type();
2441 let i32_ty = self.context.i32_type();
2442
2443 let dst_ptr = self
2445 .builder
2446 .build_pointer_cast(var_data_ptr, ptr_ty, "md_dst_ptr")
2447 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2448 let base_src_ptr = self
2449 .builder
2450 .build_int_to_ptr(*src_addr, ptr_ty, "md_src_ptr")
2451 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2452 let offsets_found = self.load_offsets_found_flag()?;
2453 let not_found = self
2454 .builder
2455 .build_not(offsets_found, "md_offsets_miss")
2456 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2457 let null_ptr = ptr_ty.const_null();
2458 let src_ptr = self
2459 .builder
2460 .build_select::<BasicValueEnum<'ctx>, _>(
2461 offsets_found,
2462 base_src_ptr.into(),
2463 null_ptr.into(),
2464 "md_src_or_null",
2465 )
2466 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
2467 .into_pointer_value();
2468 let len_const = i32_ty.const_int(*len as u64, false);
2469 let zero_i32 = i32_ty.const_zero();
2470 let effective_len = self
2471 .builder
2472 .build_select::<BasicValueEnum<'ctx>, _>(
2473 offsets_found,
2474 len_const.into(),
2475 zero_i32.into(),
2476 "md_len_or_zero",
2477 )
2478 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
2479 .into_int_value();
2480 let ret = self
2481 .create_bpf_helper_call(
2482 aya_ebpf_bindings::bindings::bpf_func_id::BPF_FUNC_probe_read_user
2483 as u64,
2484 &[dst_ptr.into(), effective_len.into(), src_ptr.into()],
2485 i64_ty.into(),
2486 "probe_read_user_memdump",
2487 )?
2488 .into_int_value();
2489
2490 let ok_pred = self
2492 .builder
2493 .build_int_compare(
2494 inkwell::IntPredicate::EQ,
2495 ret,
2496 i64_ty.const_zero(),
2497 "md_ok",
2498 )
2499 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2500 let ok = self
2501 .builder
2502 .build_and(ok_pred, offsets_found, "md_ok_with_offsets")
2503 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2504 let curr = self.builder.get_insert_block().unwrap();
2505 let func = curr.get_parent().unwrap();
2506 let ok_b = self.context.append_basic_block(func, "md_ok");
2507 let err_b = self.context.append_basic_block(func, "md_err");
2508 let cont_b = self.context.append_basic_block(func, "md_cont");
2509 self.builder
2510 .build_conditional_branch(ok, ok_b, err_b)
2511 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2512 self.builder.position_at_end(ok_b);
2514 self.builder
2515 .build_unconditional_branch(cont_b)
2516 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2517 self.builder.position_at_end(err_b);
2519 let offsets_err_b = self.context.append_basic_block(func, "md_offsets_err");
2520 let helper_err_b = self.context.append_basic_block(func, "md_helper_err");
2521 self.builder
2522 .build_conditional_branch(not_found, offsets_err_b, helper_err_b)
2523 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2524 self.builder.position_at_end(offsets_err_b);
2525 self.builder
2526 .build_store(
2527 apl_ptr,
2528 self.context
2529 .i8_type()
2530 .const_int(VariableStatus::OffsetsUnavailable as u64, false),
2531 )
2532 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2533 self.mark_any_fail()?;
2534 self.builder
2535 .build_unconditional_branch(cont_b)
2536 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2537 self.builder.position_at_end(helper_err_b);
2538 self.builder
2539 .build_store(
2540 apl_ptr,
2541 self.context
2542 .i8_type()
2543 .const_int(VariableStatus::ReadError as u64, false),
2544 )
2545 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2546 let errno_ptr = self
2548 .builder
2549 .build_pointer_cast(
2550 var_data_ptr,
2551 self.context.ptr_type(AddressSpace::default()),
2552 "errno_ptr",
2553 )
2554 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2555 self.builder
2556 .build_store(errno_ptr, ret)
2557 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2558 let addr_ptr_i8 = unsafe {
2559 self.builder
2560 .build_gep(
2561 self.context.i8_type(),
2562 var_data_ptr,
2563 &[self.context.i32_type().const_int(4, false)],
2564 "addr_ptr_i8",
2565 )
2566 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
2567 };
2568 let addr_ptr = self
2569 .builder
2570 .build_pointer_cast(
2571 addr_ptr_i8,
2572 self.context.ptr_type(AddressSpace::default()),
2573 "addr_ptr",
2574 )
2575 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2576 self.builder
2577 .build_store(addr_ptr, *src_addr)
2578 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2579 self.mark_any_fail()?;
2580 self.builder
2581 .build_unconditional_branch(cont_b)
2582 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2583 self.builder.position_at_end(cont_b);
2584 }
2585 ComplexArgSource::MemDumpDynamic {
2586 src_addr,
2587 len_value,
2588 max_len: _,
2589 } => {
2590 let eff_max_len = effective_reserved[arg_index] as u32;
2592 let i32_ty = self.context.i32_type();
2594 let rlen_i32 = if len_value.get_type().get_bit_width() > 32 {
2595 self.builder
2596 .build_int_truncate(*len_value, i32_ty, "mdd_len_trunc")
2597 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
2598 } else if len_value.get_type().get_bit_width() < 32 {
2599 self.builder
2600 .build_int_z_extend(*len_value, i32_ty, "mdd_len_zext")
2601 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
2602 } else {
2603 *len_value
2604 };
2605 let zero_i32 = i32_ty.const_zero();
2607 let is_neg = self
2608 .builder
2609 .build_int_compare(
2610 inkwell::IntPredicate::SLT,
2611 rlen_i32,
2612 zero_i32,
2613 "mdd_len_neg",
2614 )
2615 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2616 let rlen_nn = self
2617 .builder
2618 .build_select(is_neg, zero_i32, rlen_i32, "mdd_len_nn")
2619 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
2620 .into_int_value();
2621
2622 let max_const = i32_ty.const_int(eff_max_len as u64, false);
2624 let gt = self
2625 .builder
2626 .build_int_compare(inkwell::IntPredicate::UGT, rlen_nn, max_const, "mdd_gt")
2627 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2628 let sel_len = self
2629 .builder
2630 .build_select(gt, max_const, rlen_nn, "mdd_rlen")
2631 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
2632 .into_int_value();
2633
2634 let curr = self.builder.get_insert_block().unwrap();
2636 let func = curr.get_parent().unwrap();
2637 let zero_b = self.context.append_basic_block(func, "mdd_len_zero");
2638 let read_b = self.context.append_basic_block(func, "mdd_len_read");
2639 let cont_b = self.context.append_basic_block(func, "mdd_cont");
2640 let is_zero = self
2641 .builder
2642 .build_int_compare(
2643 inkwell::IntPredicate::EQ,
2644 sel_len,
2645 i32_ty.const_zero(),
2646 "mdd_len_zero",
2647 )
2648 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2649 self.builder
2650 .build_conditional_branch(is_zero, zero_b, read_b)
2651 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2652
2653 self.builder.position_at_end(zero_b);
2655 self.builder
2656 .build_store(
2657 apl_ptr,
2658 self.context
2659 .i8_type()
2660 .const_int(VariableStatus::ZeroLength as u64, false),
2661 )
2662 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2663 self.builder
2664 .build_unconditional_branch(cont_b)
2665 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2666
2667 self.builder.position_at_end(read_b);
2669 let dst_ptr = self
2670 .builder
2671 .build_bit_cast(
2672 var_data_ptr,
2673 self.context.ptr_type(AddressSpace::default()),
2674 "mdd_dst_ptr",
2675 )
2676 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2677 let ptr_ty = self.context.ptr_type(AddressSpace::default());
2678 let base_src_ptr = self
2679 .builder
2680 .build_int_to_ptr(*src_addr, ptr_ty, "mdd_src_ptr")
2681 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2682 let offsets_found = self.load_offsets_found_flag()?;
2683 let not_found = self
2684 .builder
2685 .build_not(offsets_found, "mdd_dyn_offsets_miss")
2686 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2687 let null_ptr = ptr_ty.const_null();
2688 let src_ptr = self
2689 .builder
2690 .build_select::<BasicValueEnum<'ctx>, _>(
2691 offsets_found,
2692 base_src_ptr.into(),
2693 null_ptr.into(),
2694 "mdd_src_or_null",
2695 )
2696 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
2697 .into_pointer_value();
2698 let zero_i32 = self.context.i32_type().const_zero();
2699 let effective_len = self
2700 .builder
2701 .build_select::<BasicValueEnum<'ctx>, _>(
2702 offsets_found,
2703 sel_len.into(),
2704 zero_i32.into(),
2705 "mdd_len_or_zero",
2706 )
2707 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
2708 .into_int_value();
2709 let ret = self
2710 .create_bpf_helper_call(
2711 BPF_FUNC_probe_read_user as u64,
2712 &[dst_ptr, effective_len.into(), src_ptr.into()],
2713 self.context.i64_type().into(),
2714 "probe_read_user_dyn",
2715 )?
2716 .into_int_value();
2717 let ok_pred = self
2718 .builder
2719 .build_int_compare(
2720 inkwell::IntPredicate::EQ,
2721 ret,
2722 self.context.i64_type().const_zero(),
2723 "mdd_ok",
2724 )
2725 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2726 let ok = self
2727 .builder
2728 .build_and(ok_pred, offsets_found, "mdd_ok_with_offsets")
2729 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2730 let ok_b = self.context.append_basic_block(func, "mdd_ok");
2731 let err_b = self.context.append_basic_block(func, "mdd_err");
2732 self.builder
2733 .build_conditional_branch(ok, ok_b, err_b)
2734 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2735 self.builder.position_at_end(ok_b);
2737 self.builder
2738 .build_unconditional_branch(cont_b)
2739 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2740 self.builder.position_at_end(err_b);
2742 let offsets_err_b = self.context.append_basic_block(func, "mdd_offsets_err");
2743 let helper_err_b = self.context.append_basic_block(func, "mdd_helper_err");
2744 self.builder
2745 .build_conditional_branch(not_found, offsets_err_b, helper_err_b)
2746 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2747 self.builder.position_at_end(offsets_err_b);
2748 self.builder
2749 .build_store(
2750 apl_ptr,
2751 self.context
2752 .i8_type()
2753 .const_int(VariableStatus::OffsetsUnavailable as u64, false),
2754 )
2755 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2756 self.mark_any_fail()?;
2757 self.builder
2758 .build_unconditional_branch(cont_b)
2759 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2760 self.builder.position_at_end(helper_err_b);
2761 self.builder
2762 .build_store(
2763 apl_ptr,
2764 self.context
2765 .i8_type()
2766 .const_int(VariableStatus::ReadError as u64, false),
2767 )
2768 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2769 let errno_ptr = self
2770 .builder
2771 .build_pointer_cast(
2772 var_data_ptr,
2773 self.context.ptr_type(AddressSpace::default()),
2774 "mdd_errno_ptr",
2775 )
2776 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2777 self.builder
2778 .build_store(errno_ptr, ret)
2779 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2780 let addr_ptr_i8 = unsafe {
2781 self.builder
2782 .build_gep(
2783 self.context.i8_type(),
2784 var_data_ptr,
2785 &[self.context.i32_type().const_int(4, false)],
2786 "mdd_addr_ptr_i8",
2787 )
2788 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
2789 };
2790 let addr_ptr = self
2791 .builder
2792 .build_pointer_cast(
2793 addr_ptr_i8,
2794 self.context.ptr_type(AddressSpace::default()),
2795 "mdd_addr_ptr",
2796 )
2797 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2798 self.builder
2799 .build_store(addr_ptr, *src_addr)
2800 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2801 self.mark_any_fail()?;
2802 self.builder
2803 .build_unconditional_branch(cont_b)
2804 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2805 self.builder.position_at_end(cont_b);
2806 }
2807 ComplexArgSource::ComputedInt { value, byte_len } => {
2808 match *byte_len {
2811 1 => {
2812 let bitw = value.get_type().get_bit_width();
2813 let v = if bitw == 1 {
2814 self.builder
2816 .build_int_z_extend(
2817 *value,
2818 self.context.i8_type(),
2819 "expr_zext_bool_i8",
2820 )
2821 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
2822 } else if bitw < 8 {
2823 self.builder
2824 .build_int_s_extend(
2825 *value,
2826 self.context.i8_type(),
2827 "expr_sext_i8",
2828 )
2829 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
2830 } else if bitw > 8 {
2831 self.builder
2833 .build_int_truncate(
2834 *value,
2835 self.context.i8_type(),
2836 "expr_trunc_i8",
2837 )
2838 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
2839 } else {
2840 *value
2842 };
2843 self.builder
2845 .build_store(var_data_ptr, v)
2846 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2847 }
2848 2 => {
2849 let bitw = value.get_type().get_bit_width();
2850 let v = if bitw < 16 {
2851 self.builder
2852 .build_int_s_extend(
2853 *value,
2854 self.context.i16_type(),
2855 "expr_sext_i16",
2856 )
2857 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
2858 } else if bitw > 16 {
2859 self.builder
2860 .build_int_truncate(
2861 *value,
2862 self.context.i16_type(),
2863 "expr_trunc_i16",
2864 )
2865 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
2866 } else {
2867 *value
2869 };
2870 let i16_ptr_ty = self.context.ptr_type(AddressSpace::default());
2871 let cast_ptr = self
2872 .builder
2873 .build_pointer_cast(var_data_ptr, i16_ptr_ty, "expr_i16_ptr")
2874 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2875 self.builder
2876 .build_store(cast_ptr, v)
2877 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2878 }
2879 4 => {
2880 let bitw = value.get_type().get_bit_width();
2881 let v = if bitw < 32 {
2882 self.builder
2883 .build_int_s_extend(
2884 *value,
2885 self.context.i32_type(),
2886 "expr_sext_i32",
2887 )
2888 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
2889 } else if bitw > 32 {
2890 self.builder
2891 .build_int_truncate(
2892 *value,
2893 self.context.i32_type(),
2894 "expr_trunc_i32",
2895 )
2896 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
2897 } else {
2898 *value
2900 };
2901 let i32_ptr_ty = self.context.ptr_type(AddressSpace::default());
2902 let cast_ptr = self
2903 .builder
2904 .build_pointer_cast(var_data_ptr, i32_ptr_ty, "expr_i32_ptr")
2905 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2906 self.builder
2907 .build_store(cast_ptr, v)
2908 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2909 }
2910 8 => {
2911 let v64 = if value.get_type().get_bit_width() < 64 {
2912 self.builder
2913 .build_int_s_extend(
2914 *value,
2915 self.context.i64_type(),
2916 "expr_sext",
2917 )
2918 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
2919 } else {
2920 *value
2921 };
2922 let i64_ptr_ty = self.context.ptr_type(AddressSpace::default());
2923 let cast_ptr = self
2924 .builder
2925 .build_pointer_cast(var_data_ptr, i64_ptr_ty, "expr_i64_ptr")
2926 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2927 self.builder
2928 .build_store(cast_ptr, v64)
2929 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2930 }
2931 n => {
2932 let v64 = if value.get_type().get_bit_width() < 64 {
2935 self.builder
2936 .build_int_z_extend(
2937 *value,
2938 self.context.i64_type(),
2939 "expr_zext_fallback",
2940 )
2941 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
2942 } else {
2943 *value
2944 };
2945 for i in 0..n {
2946 let shift =
2948 self.context.i64_type().const_int((i * 8) as u64, false);
2949 let shifted = self
2950 .builder
2951 .build_right_shift(v64, shift, false, &format!("expr_shr_{i}"))
2952 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2953 let byte = self
2954 .builder
2955 .build_int_truncate(
2956 shifted,
2957 self.context.i8_type(),
2958 &format!("expr_byte_{i}"),
2959 )
2960 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2961 let byte_ptr = unsafe {
2962 self.builder
2963 .build_gep(
2964 self.context.i8_type(),
2965 var_data_ptr,
2966 &[self.context.i32_type().const_int(i as u64, false)],
2967 &format!("expr_byte_ptr_{i}"),
2968 )
2969 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
2970 };
2971 self.builder
2972 .build_store(byte_ptr, byte)
2973 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2974 }
2975 }
2976 }
2977 }
2978 ComplexArgSource::RuntimeRead {
2979 eval_result,
2980 dwarf_type,
2981 module_for_offsets,
2982 } => {
2983 let ptr_type = self.context.ptr_type(AddressSpace::default());
2985 let i32_type = self.context.i32_type();
2986 let i64_type = self.context.i64_type();
2987 let dst_ptr = self
2988 .builder
2989 .build_bit_cast(var_data_ptr, ptr_type, "dst_ptr")
2990 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2991 let size_val = i32_type.const_int(a.data_len as u64, false);
2992 let src_addr = self.evaluation_result_to_address_with_hint(
2994 eval_result,
2995 Some(apl_ptr),
2996 module_for_offsets.as_deref(),
2997 )?;
2998 let offsets_found = self.load_offsets_found_flag()?;
2999 let current_block = self.builder.get_insert_block().unwrap();
3000 let current_fn = current_block.get_parent().unwrap();
3001 let cont2_block = self.context.append_basic_block(current_fn, "after_read");
3002 let skip_block = self.context.append_basic_block(current_fn, "offsets_skip");
3003 let found_block = self.context.append_basic_block(current_fn, "offsets_found");
3004 self.builder
3005 .build_conditional_branch(offsets_found, found_block, skip_block)
3006 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3007
3008 self.builder.position_at_end(skip_block);
3010 self.mark_any_fail()?;
3011 self.builder
3012 .build_unconditional_branch(cont2_block)
3013 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3014
3015 self.builder.position_at_end(found_block);
3017 let src_ptr = self
3018 .builder
3019 .build_int_to_ptr(src_addr, ptr_type, "src_ptr")
3020 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3021
3022 let zero64 = i64_type.const_zero();
3025 let is_null = self
3026 .builder
3027 .build_int_compare(inkwell::IntPredicate::EQ, src_addr, zero64, "is_null")
3028 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3029 let null_block = self.context.append_basic_block(current_fn, "null_deref");
3030 let read_block = self.context.append_basic_block(current_fn, "read_user");
3031 self.builder
3032 .build_conditional_branch(is_null, null_block, read_block)
3033 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3034
3035 self.builder.position_at_end(null_block);
3037 self.builder
3038 .build_store(
3039 apl_ptr,
3040 self.context
3041 .i8_type()
3042 .const_int(VariableStatus::NullDeref as u64, false),
3043 )
3044 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3045 self.mark_any_fail()?;
3046 self.builder
3047 .build_unconditional_branch(cont2_block)
3048 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3049
3050 self.builder.position_at_end(read_block);
3052 let ret = self
3053 .create_bpf_helper_call(
3054 BPF_FUNC_probe_read_user as u64,
3055 &[dst_ptr, size_val.into(), src_ptr.into()],
3056 i32_type.into(),
3057 "probe_read_user",
3058 )?
3059 .into_int_value();
3060 let is_err = self
3061 .builder
3062 .build_int_compare(
3063 inkwell::IntPredicate::SLT,
3064 ret,
3065 i32_type.const_zero(),
3066 "ret_lt_zero",
3067 )
3068 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3069 let err_block = self.context.append_basic_block(current_fn, "read_err");
3070 let ok_block = self.context.append_basic_block(current_fn, "read_ok");
3071 self.builder
3072 .build_conditional_branch(is_err, err_block, ok_block)
3073 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3074
3075 self.builder.position_at_end(err_block);
3077 self.builder
3078 .build_store(
3079 apl_ptr,
3080 self.context
3081 .i8_type()
3082 .const_int(VariableStatus::ReadError as u64, false),
3083 )
3084 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3085 let i32_ptr = self
3087 .builder
3088 .build_pointer_cast(
3089 var_data_ptr,
3090 self.context.ptr_type(AddressSpace::default()),
3091 "errno_ptr",
3092 )
3093 .map_err(|e| {
3094 CodeGenError::LLVMError(format!("Failed to cast errno ptr: {e}"))
3095 })?;
3096 self.builder.build_store(i32_ptr, ret).map_err(|e| {
3097 CodeGenError::LLVMError(format!("Failed to store errno: {e}"))
3098 })?;
3099 let addr_ptr_i8 = unsafe {
3101 self.builder
3102 .build_gep(
3103 self.context.i8_type(),
3104 var_data_ptr,
3105 &[i32_type.const_int(4, false)],
3106 "addr_ptr_i8",
3107 )
3108 .map_err(|e| {
3109 CodeGenError::LLVMError(format!("Failed to get addr gep: {e}"))
3110 })?
3111 };
3112 let addr_ptr = self
3113 .builder
3114 .build_pointer_cast(
3115 addr_ptr_i8,
3116 self.context.ptr_type(AddressSpace::default()),
3117 "addr_ptr",
3118 )
3119 .map_err(|e| {
3120 CodeGenError::LLVMError(format!("Failed to cast addr ptr: {e}"))
3121 })?;
3122 let src_as_i64 = src_addr;
3123 self.builder
3124 .build_store(addr_ptr, src_as_i64)
3125 .map_err(|e| {
3126 CodeGenError::LLVMError(format!("Failed to store addr: {e}"))
3127 })?;
3128 self.mark_any_fail()?;
3129 self.builder
3130 .build_unconditional_branch(cont2_block)
3131 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3132
3133 self.builder.position_at_end(ok_block);
3135 if a.data_len < dwarf_type.size() as usize {
3136 self.builder
3137 .build_store(
3138 apl_ptr,
3139 self.context
3140 .i8_type()
3141 .const_int(VariableStatus::Truncated as u64, false),
3142 )
3143 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3144 self.mark_any_success()?;
3145 self.mark_any_fail()?;
3146 } else {
3147 self.mark_any_success()?;
3148 }
3149 self.builder
3150 .build_unconditional_branch(cont2_block)
3151 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3152
3153 self.builder.position_at_end(cont2_block);
3154 }
3155 ComplexArgSource::AddressValue {
3156 eval_result,
3157 module_for_offsets,
3158 } => {
3159 let addr = self.evaluation_result_to_address_with_hint(
3161 eval_result,
3162 Some(apl_ptr),
3163 module_for_offsets.as_deref(),
3164 )?;
3165 let cast_ptr = self
3166 .builder
3167 .build_pointer_cast(
3168 var_data_ptr,
3169 self.context.ptr_type(AddressSpace::default()),
3170 "addr_store_ptr",
3171 )
3172 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3173 self.builder
3174 .build_store(cast_ptr, addr)
3175 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3176 }
3178 }
3179 offset += 2 + 2 + 1 + 1 + a.access_path.len() + 2 + reserved_len;
3181 }
3182
3183 Ok(())
3185 }
3186
3187 pub fn generate_print_string_index(&mut self, string_index: u16) -> Result<()> {
3189 info!(
3190 "Generating PrintStringIndex instruction: index={}",
3191 string_index
3192 );
3193
3194 let inst_buffer = self.reserve_instruction_region(
3197 (std::mem::size_of::<InstructionHeader>() + std::mem::size_of::<PrintStringIndexData>())
3198 as u64,
3199 );
3200
3201 let _inst_size = self.context.i64_type().const_int(
3203 (std::mem::size_of::<PrintStringIndexData>()
3204 + std::mem::size_of::<ghostscope_protocol::trace_event::InstructionHeader>())
3205 as u64,
3206 false,
3207 );
3208 let inst_type_ptr = unsafe {
3213 self.builder
3214 .build_gep(
3215 self.context.i8_type(),
3216 inst_buffer,
3217 &[self.context.i32_type().const_int(
3218 std::mem::offset_of!(InstructionHeader, inst_type) as u64,
3219 false,
3220 )],
3221 "inst_type_ptr",
3222 )
3223 .map_err(|e| CodeGenError::LLVMError(format!("Failed to get inst_type GEP: {e}")))?
3224 };
3225 let inst_type_val = self
3226 .context
3227 .i8_type()
3228 .const_int(InstructionType::PrintStringIndex as u64, false);
3229 self.builder
3230 .build_store(inst_type_ptr, inst_type_val)
3231 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store inst_type: {e}")))?;
3232
3233 let data_length_ptr = unsafe {
3234 self.builder
3235 .build_gep(
3236 self.context.i8_type(),
3237 inst_buffer,
3238 &[self.context.i32_type().const_int(
3239 std::mem::offset_of!(InstructionHeader, data_length) as u64,
3240 false,
3241 )],
3242 "data_length_ptr",
3243 )
3244 .map_err(|e| {
3245 CodeGenError::LLVMError(format!("Failed to get data_length GEP: {e}"))
3246 })?
3247 };
3248 let data_length_i16_ptr = self
3249 .builder
3250 .build_pointer_cast(
3251 data_length_ptr,
3252 self.context.ptr_type(AddressSpace::default()),
3253 "data_length_i16_ptr",
3254 )
3255 .map_err(|e| CodeGenError::LLVMError(format!("Failed to cast data_length ptr: {e}")))?;
3256 let data_length_val = self
3257 .context
3258 .i16_type()
3259 .const_int(std::mem::size_of::<PrintStringIndexData>() as u64, false);
3260 self.builder
3261 .build_store(data_length_i16_ptr, data_length_val)
3262 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store data_length: {e}")))?;
3263
3264 let string_index_ptr = unsafe {
3266 self.builder
3267 .build_gep(
3268 self.context.i8_type(),
3269 inst_buffer,
3270 &[self
3271 .context
3272 .i32_type()
3273 .const_int(std::mem::size_of::<InstructionHeader>() as u64, false)],
3274 "string_index_ptr",
3275 )
3276 .map_err(|e| {
3277 CodeGenError::LLVMError(format!("Failed to get string_index GEP: {e}"))
3278 })?
3279 };
3280 let string_index_i16_ptr = self
3281 .builder
3282 .build_pointer_cast(
3283 string_index_ptr,
3284 self.context.ptr_type(AddressSpace::default()),
3285 "string_index_i16_ptr",
3286 )
3287 .map_err(|e| {
3288 CodeGenError::LLVMError(format!("Failed to cast string_index ptr: {e}"))
3289 })?;
3290 let string_index_val = self
3291 .context
3292 .i16_type()
3293 .const_int(string_index as u64, false);
3294 self.builder
3295 .build_store(string_index_i16_ptr, string_index_val)
3296 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store string_index: {e}")))?;
3297
3298 Ok(())
3300 }
3301
3302 pub fn generate_expr_error(
3304 &mut self,
3305 expr_string_index: u16,
3306 error_code_iv: inkwell::values::IntValue<'ctx>,
3307 flags_iv: inkwell::values::IntValue<'ctx>,
3308 failing_addr_iv: inkwell::values::IntValue<'ctx>,
3309 ) -> Result<()> {
3310 let inst_buffer = self.reserve_instruction_region(
3312 (std::mem::size_of::<InstructionHeader>()
3313 + std::mem::size_of::<ghostscope_protocol::trace_event::ExprErrorData>())
3314 as u64,
3315 );
3316
3317 let inst_type_val = self
3319 .context
3320 .i8_type()
3321 .const_int(InstructionType::ExprError as u64, false);
3322 self.builder
3323 .build_store(inst_buffer, inst_type_val)
3324 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store inst_type: {e}")))?;
3325
3326 let data_length_ptr = unsafe {
3328 self.builder
3329 .build_gep(
3330 self.context.i8_type(),
3331 inst_buffer,
3332 &[self.context.i32_type().const_int(
3333 std::mem::offset_of!(InstructionHeader, data_length) as u64,
3334 false,
3335 )],
3336 "exprerr_data_length_ptr",
3337 )
3338 .map_err(|e| {
3339 CodeGenError::LLVMError(format!("Failed to get data_length GEP: {e}"))
3340 })?
3341 };
3342 let data_length_i16_ptr = self
3343 .builder
3344 .build_pointer_cast(
3345 data_length_ptr,
3346 self.context.ptr_type(AddressSpace::default()),
3347 "exprerr_data_length_i16_ptr",
3348 )
3349 .map_err(|e| CodeGenError::LLVMError(format!("Failed to cast data_length ptr: {e}")))?;
3350 let data_length_val = self.context.i16_type().const_int(
3351 std::mem::size_of::<ghostscope_protocol::trace_event::ExprErrorData>() as u64,
3352 false,
3353 );
3354 self.builder
3355 .build_store(data_length_i16_ptr, data_length_val)
3356 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store data_length: {e}")))?;
3357
3358 let si_ptr = unsafe {
3361 self.builder
3362 .build_gep(
3363 self.context.i8_type(),
3364 inst_buffer,
3365 &[self
3366 .context
3367 .i32_type()
3368 .const_int(std::mem::size_of::<InstructionHeader>() as u64, false)],
3369 "exprerr_si_ptr",
3370 )
3371 .map_err(|e| {
3372 CodeGenError::LLVMError(format!("Failed to get string_index GEP: {e}"))
3373 })?
3374 };
3375 let si_i16_ptr = self
3376 .builder
3377 .build_pointer_cast(
3378 si_ptr,
3379 self.context.ptr_type(AddressSpace::default()),
3380 "exprerr_si_i16_ptr",
3381 )
3382 .map_err(|e| {
3383 CodeGenError::LLVMError(format!("Failed to cast string_index ptr: {e}"))
3384 })?;
3385 let si_val = self
3386 .context
3387 .i16_type()
3388 .const_int(expr_string_index as u64, false);
3389 self.builder
3390 .build_store(si_i16_ptr, si_val)
3391 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store string_index: {e}")))?;
3392
3393 let ec_ptr = unsafe {
3395 self.builder
3396 .build_gep(
3397 self.context.i8_type(),
3398 inst_buffer,
3399 &[self
3400 .context
3401 .i32_type()
3402 .const_int((std::mem::size_of::<InstructionHeader>() + 2) as u64, false)],
3403 "exprerr_ec_ptr",
3404 )
3405 .map_err(|e| {
3406 CodeGenError::LLVMError(format!("Failed to get error_code GEP: {e}"))
3407 })?
3408 };
3409 let ec_i8 = if error_code_iv.get_type().get_bit_width() == 8 {
3411 error_code_iv
3412 } else if error_code_iv.get_type().get_bit_width() > 8 {
3413 self.builder
3414 .build_int_truncate(error_code_iv, self.context.i8_type(), "ec_trunc")
3415 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
3416 } else {
3417 self.builder
3418 .build_int_z_extend(error_code_iv, self.context.i8_type(), "ec_zext")
3419 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
3420 };
3421 self.builder
3422 .build_store(ec_ptr, ec_i8)
3423 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store error_code: {e}")))?;
3424 let fl_ptr = unsafe {
3425 self.builder
3426 .build_gep(
3427 self.context.i8_type(),
3428 inst_buffer,
3429 &[self
3430 .context
3431 .i32_type()
3432 .const_int((std::mem::size_of::<InstructionHeader>() + 3) as u64, false)],
3433 "exprerr_flags_ptr",
3434 )
3435 .map_err(|e| CodeGenError::LLVMError(format!("Failed to get flags GEP: {e}")))?
3436 };
3437 let fl_i8 = if flags_iv.get_type().get_bit_width() == 8 {
3439 flags_iv
3440 } else if flags_iv.get_type().get_bit_width() > 8 {
3441 self.builder
3442 .build_int_truncate(flags_iv, self.context.i8_type(), "fl_trunc")
3443 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
3444 } else {
3445 self.builder
3446 .build_int_z_extend(flags_iv, self.context.i8_type(), "fl_zext")
3447 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
3448 };
3449 self.builder
3450 .build_store(fl_ptr, fl_i8)
3451 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store flags: {e}")))?;
3452
3453 let addr_ptr = unsafe {
3455 self.builder
3456 .build_gep(
3457 self.context.i8_type(),
3458 inst_buffer,
3459 &[self
3460 .context
3461 .i32_type()
3462 .const_int((std::mem::size_of::<InstructionHeader>() + 4) as u64, false)],
3463 "exprerr_addr_ptr",
3464 )
3465 .map_err(|e| CodeGenError::LLVMError(format!("Failed to get addr GEP: {e}")))?
3466 };
3467 let addr_i64 = if failing_addr_iv.get_type().get_bit_width() == 64 {
3468 failing_addr_iv
3469 } else if failing_addr_iv.get_type().get_bit_width() > 64 {
3470 self.builder
3471 .build_int_truncate(failing_addr_iv, self.context.i64_type(), "addr_trunc")
3472 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
3473 } else {
3474 self.builder
3475 .build_int_z_extend(failing_addr_iv, self.context.i64_type(), "addr_zext")
3476 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
3477 };
3478 let addr_ptr_cast = self
3479 .builder
3480 .build_pointer_cast(
3481 addr_ptr,
3482 self.context.ptr_type(AddressSpace::default()),
3483 "exprerr_addr_i64_ptr",
3484 )
3485 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
3486 self.builder
3487 .build_store(addr_ptr_cast, addr_i64)
3488 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store failing_addr: {e}")))?;
3489
3490 Ok(())
3492 }
3493
3494 pub fn generate_print_variable_index(
3496 &mut self,
3497 var_name_index: u16,
3498 type_encoding: TypeKind,
3499 var_name: &str,
3500 ) -> Result<()> {
3501 info!(
3502 "Generating PrintVariableIndex instruction: var_name_index={}, type={:?}, var_name={}",
3503 var_name_index, type_encoding, var_name
3504 );
3505
3506 let type_index = match self.query_dwarf_for_variable(var_name)? {
3508 Some(var) => match var.dwarf_type {
3509 Some(ref t) => self.trace_context.add_type(t.clone()),
3510 None => self.add_synthesized_type_index_for_kind(type_encoding),
3511 },
3512 None => {
3513 self.add_synthesized_type_index_for_kind(type_encoding)
3515 }
3516 };
3517
3518 match self.resolve_variable_value(var_name, type_encoding) {
3519 Ok(var_data) => self.generate_successful_variable_instruction(
3520 var_name_index,
3521 type_encoding,
3522 type_index,
3523 var_data,
3524 ),
3525 Err(e) => Err(e),
3526 }
3527 }
3528
3529 fn generate_successful_variable_instruction(
3531 &mut self,
3532 var_name_index: u16,
3533 type_encoding: TypeKind,
3534 type_index: u16,
3535 var_data: BasicValueEnum<'ctx>,
3536 ) -> Result<()> {
3537 let data_size = match type_encoding {
3539 TypeKind::U8 | TypeKind::I8 | TypeKind::Bool | TypeKind::Char => 1,
3540 TypeKind::U16 | TypeKind::I16 => 2,
3541 TypeKind::U32 | TypeKind::I32 | TypeKind::F32 => 4,
3542 TypeKind::U64 | TypeKind::I64 | TypeKind::F64 | TypeKind::Pointer => 8,
3543 _ => 8, };
3545
3546 let inst_buffer = self.reserve_instruction_region(
3548 (std::mem::size_of::<InstructionHeader>()
3549 + std::mem::size_of::<PrintVariableIndexData>()
3550 + data_size as usize) as u64,
3551 );
3552
3553 let inst_type_val = self
3557 .context
3558 .i8_type()
3559 .const_int(InstructionType::PrintVariableIndex as u64, false);
3560 self.builder
3561 .build_store(inst_buffer, inst_type_val)
3562 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store inst_type: {e}")))?;
3563
3564 let data_length_ptr = unsafe {
3566 self.builder
3567 .build_gep(
3568 self.context.i8_type(),
3569 inst_buffer,
3570 &[self.context.i32_type().const_int(
3571 std::mem::offset_of!(InstructionHeader, data_length) as u64,
3572 false,
3573 )],
3574 "data_length_ptr",
3575 )
3576 .map_err(|e| {
3577 CodeGenError::LLVMError(format!("Failed to get data_length GEP: {e}"))
3578 })?
3579 };
3580 let data_length_i16_ptr = self
3581 .builder
3582 .build_pointer_cast(
3583 data_length_ptr,
3584 self.context.ptr_type(AddressSpace::default()),
3585 "data_length_i16_ptr",
3586 )
3587 .map_err(|e| CodeGenError::LLVMError(format!("Failed to cast data_length ptr: {e}")))?;
3588 let total_data_length = std::mem::size_of::<PrintVariableIndexData>() + data_size as usize;
3589 let data_length_val = self
3590 .context
3591 .i16_type()
3592 .const_int(total_data_length as u64, false);
3593 self.builder
3594 .build_store(data_length_i16_ptr, data_length_val)
3595 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store data_length: {e}")))?;
3596
3597 let variable_data_start = unsafe {
3599 self.builder
3600 .build_gep(
3601 self.context.i8_type(),
3602 inst_buffer,
3603 &[self
3604 .context
3605 .i32_type()
3606 .const_int(std::mem::size_of::<InstructionHeader>() as u64, false)],
3607 "variable_data_start",
3608 )
3609 .map_err(|e| {
3610 CodeGenError::LLVMError(format!("Failed to get variable_data_start GEP: {e}"))
3611 })?
3612 };
3613
3614 let var_name_index_ptr = unsafe {
3616 self.builder
3617 .build_gep(
3618 self.context.i8_type(),
3619 variable_data_start,
3620 &[self.context.i32_type().const_int(
3621 std::mem::offset_of!(PrintVariableIndexData, var_name_index) as u64,
3622 false,
3623 )],
3624 "var_name_index_ptr",
3625 )
3626 .map_err(|e| {
3627 CodeGenError::LLVMError(format!("Failed to get var_name_index GEP: {e}"))
3628 })?
3629 };
3630 let var_name_index_i16_ptr = self
3631 .builder
3632 .build_pointer_cast(
3633 var_name_index_ptr,
3634 self.context.ptr_type(AddressSpace::default()),
3635 "var_name_index_i16_ptr",
3636 )
3637 .map_err(|e| {
3638 CodeGenError::LLVMError(format!("Failed to cast var_name_index ptr: {e}"))
3639 })?;
3640 let var_name_index_val = self
3641 .context
3642 .i16_type()
3643 .const_int(var_name_index as u64, false);
3644 self.builder
3645 .build_store(var_name_index_i16_ptr, var_name_index_val)
3646 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store var_name_index: {e}")))?;
3647
3648 let type_encoding_ptr = unsafe {
3650 self.builder
3651 .build_gep(
3652 self.context.i8_type(),
3653 variable_data_start,
3654 &[self.context.i32_type().const_int(
3655 std::mem::offset_of!(PrintVariableIndexData, type_encoding) as u64,
3656 false,
3657 )],
3658 "type_encoding_ptr",
3659 )
3660 .map_err(|e| {
3661 CodeGenError::LLVMError(format!("Failed to get type_encoding GEP: {e}"))
3662 })?
3663 };
3664 let type_encoding_val = self
3665 .context
3666 .i8_type()
3667 .const_int(type_encoding as u8 as u64, false);
3668 self.builder
3669 .build_store(type_encoding_ptr, type_encoding_val)
3670 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store type_encoding: {e}")))?;
3671
3672 let data_len_ptr = unsafe {
3674 self.builder
3675 .build_gep(
3676 self.context.i8_type(),
3677 variable_data_start,
3678 &[self.context.i32_type().const_int(
3679 std::mem::offset_of!(PrintVariableIndexData, data_len) as u64,
3680 false,
3681 )],
3682 "data_len_ptr",
3683 )
3684 .map_err(|e| CodeGenError::LLVMError(format!("Failed to get data_len GEP: {e}")))?
3685 };
3686 let data_len_i16_ptr = self
3687 .builder
3688 .build_pointer_cast(
3689 data_len_ptr,
3690 self.context.ptr_type(AddressSpace::default()),
3691 "data_len_i16_ptr",
3692 )
3693 .map_err(|e| CodeGenError::LLVMError(format!("Failed to cast data_len ptr: {e}")))?;
3694 let data_len_val = self.context.i16_type().const_int(data_size as u64, false); self.builder
3696 .build_store(data_len_i16_ptr, data_len_val)
3697 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store data_len: {e}")))?;
3698
3699 let type_index_ptr = unsafe {
3701 self.builder
3702 .build_gep(
3703 self.context.i8_type(),
3704 variable_data_start,
3705 &[self.context.i32_type().const_int(
3706 std::mem::offset_of!(PrintVariableIndexData, type_index) as u64,
3707 false,
3708 )],
3709 "type_index_ptr",
3710 )
3711 .map_err(|e| {
3712 CodeGenError::LLVMError(format!("Failed to get type_index GEP: {e}"))
3713 })?
3714 };
3715 let type_index_i16_ptr = self
3716 .builder
3717 .build_pointer_cast(
3718 type_index_ptr,
3719 self.context.ptr_type(AddressSpace::default()),
3720 "type_index_i16_ptr",
3721 )
3722 .map_err(|e| CodeGenError::LLVMError(format!("Failed to cast type_index ptr: {e}")))?;
3723 let type_index_val = self.context.i16_type().const_int(type_index as u64, false);
3724 self.builder
3725 .build_store(type_index_i16_ptr, type_index_val)
3726 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store type_index: {e}")))?;
3727
3728 let reserved_ptr = unsafe {
3730 self.builder
3731 .build_gep(
3732 self.context.i8_type(),
3733 variable_data_start,
3734 &[self.context.i32_type().const_int(
3735 std::mem::offset_of!(PrintVariableIndexData, status) as u64,
3736 false,
3737 )],
3738 "status_ptr",
3739 )
3740 .map_err(|e| CodeGenError::LLVMError(format!("Failed to get status GEP: {e}")))?
3741 };
3742 let reserved_val = self
3743 .context
3744 .i8_type()
3745 .const_int(VariableStatus::Ok as u64, false);
3746 self.builder
3747 .build_store(reserved_ptr, reserved_val)
3748 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store status: {e}")))?;
3749
3750 let var_data_ptr = unsafe {
3752 self.builder
3753 .build_gep(
3754 self.context.i8_type(),
3755 variable_data_start,
3756 &[self
3757 .context
3758 .i32_type()
3759 .const_int(std::mem::size_of::<PrintVariableIndexData>() as u64, false)],
3760 "var_data_ptr",
3761 )
3762 .map_err(|e| CodeGenError::LLVMError(format!("Failed to get var_data GEP: {e}")))?
3763 };
3764
3765 match data_size {
3768 1 => {
3769 let truncated = match var_data {
3771 BasicValueEnum::IntValue(int_val) => self
3772 .builder
3773 .build_int_truncate(int_val, self.context.i8_type(), "truncated_i8")
3774 .map_err(|e| {
3775 CodeGenError::LLVMError(format!("Failed to truncate to i8: {e}"))
3776 })?,
3777 _ => {
3778 return Err(CodeGenError::LLVMError(
3779 "Expected integer value for integer type".to_string(),
3780 ));
3781 }
3782 };
3783 self.builder
3784 .build_store(var_data_ptr, truncated)
3785 .map_err(|e| {
3786 CodeGenError::LLVMError(format!("Failed to store i8 data: {e}"))
3787 })?;
3788 }
3789 2 => {
3790 let truncated = match var_data {
3792 BasicValueEnum::IntValue(int_val) => self
3793 .builder
3794 .build_int_truncate(int_val, self.context.i16_type(), "truncated_i16")
3795 .map_err(|e| {
3796 CodeGenError::LLVMError(format!("Failed to truncate to i16: {e}"))
3797 })?,
3798 _ => {
3799 return Err(CodeGenError::LLVMError(
3800 "Expected integer value for integer type".to_string(),
3801 ));
3802 }
3803 };
3804 let i16_ptr = self
3805 .builder
3806 .build_pointer_cast(
3807 var_data_ptr,
3808 self.context.ptr_type(AddressSpace::default()),
3809 "i16_ptr",
3810 )
3811 .map_err(|e| {
3812 CodeGenError::LLVMError(format!("Failed to cast to i16 ptr: {e}"))
3813 })?;
3814 self.builder.build_store(i16_ptr, truncated).map_err(|e| {
3815 CodeGenError::LLVMError(format!("Failed to store i16 data: {e}"))
3816 })?;
3817 }
3818 4 => {
3819 match var_data {
3821 BasicValueEnum::IntValue(int_val) => {
3822 let truncated = self
3823 .builder
3824 .build_int_truncate(int_val, self.context.i32_type(), "truncated_i32")
3825 .map_err(|e| {
3826 CodeGenError::LLVMError(format!("Failed to truncate to i32: {e}"))
3827 })?;
3828 let i32_ptr = self
3829 .builder
3830 .build_pointer_cast(
3831 var_data_ptr,
3832 self.context.ptr_type(AddressSpace::default()),
3833 "i32_ptr",
3834 )
3835 .map_err(|e| {
3836 CodeGenError::LLVMError(format!("Failed to cast to i32 ptr: {e}"))
3837 })?;
3838 self.builder.build_store(i32_ptr, truncated).map_err(|e| {
3839 CodeGenError::LLVMError(format!("Failed to store i32 data: {e}"))
3840 })?;
3841 }
3842 BasicValueEnum::FloatValue(float_val) => {
3843 let f32_ptr = self
3844 .builder
3845 .build_pointer_cast(
3846 var_data_ptr,
3847 self.context.ptr_type(AddressSpace::default()),
3848 "f32_ptr",
3849 )
3850 .map_err(|e| {
3851 CodeGenError::LLVMError(format!("Failed to cast to f32 ptr: {e}"))
3852 })?;
3853 self.builder.build_store(f32_ptr, float_val).map_err(|e| {
3854 CodeGenError::LLVMError(format!("Failed to store f32 data: {e}"))
3855 })?;
3856 }
3857 _ => {
3858 return Err(CodeGenError::LLVMError(
3859 "Expected integer or float value for 4-byte type".to_string(),
3860 ));
3861 }
3862 }
3863 }
3864 8 => {
3865 match var_data {
3867 BasicValueEnum::IntValue(int_val) => {
3868 let i64_ptr = self
3869 .builder
3870 .build_pointer_cast(
3871 var_data_ptr,
3872 self.context.ptr_type(AddressSpace::default()),
3873 "i64_ptr",
3874 )
3875 .map_err(|e| {
3876 CodeGenError::LLVMError(format!("Failed to cast to i64 ptr: {e}"))
3877 })?;
3878 self.builder.build_store(i64_ptr, int_val).map_err(|e| {
3879 CodeGenError::LLVMError(format!("Failed to store i64 data: {e}"))
3880 })?;
3881 }
3882 BasicValueEnum::FloatValue(float_val) => {
3883 let f64_ptr = self
3884 .builder
3885 .build_pointer_cast(
3886 var_data_ptr,
3887 self.context.ptr_type(AddressSpace::default()),
3888 "f64_ptr",
3889 )
3890 .map_err(|e| {
3891 CodeGenError::LLVMError(format!("Failed to cast to f64 ptr: {e}"))
3892 })?;
3893 self.builder.build_store(f64_ptr, float_val).map_err(|e| {
3894 CodeGenError::LLVMError(format!("Failed to store f64 data: {e}"))
3895 })?;
3896 }
3897 BasicValueEnum::PointerValue(ptr_val) => {
3898 let ptr_int = self
3900 .builder
3901 .build_ptr_to_int(ptr_val, self.context.i64_type(), "ptr_as_int")
3902 .map_err(|e| {
3903 CodeGenError::LLVMError(format!(
3904 "Failed to convert ptr to int: {e}"
3905 ))
3906 })?;
3907 let i64_ptr = self
3908 .builder
3909 .build_pointer_cast(
3910 var_data_ptr,
3911 self.context.ptr_type(AddressSpace::default()),
3912 "i64_ptr",
3913 )
3914 .map_err(|e| {
3915 CodeGenError::LLVMError(format!("Failed to cast to i64 ptr: {e}"))
3916 })?;
3917 self.builder.build_store(i64_ptr, ptr_int).map_err(|e| {
3918 CodeGenError::LLVMError(format!("Failed to store pointer data: {e}"))
3919 })?;
3920 }
3921 _ => {
3922 return Err(CodeGenError::LLVMError(
3923 "Expected integer, float, or pointer value for 8-byte type".to_string(),
3924 ));
3925 }
3926 }
3927 }
3928 _ => {
3929 return Err(CodeGenError::LLVMError(format!(
3930 "Unsupported data size: {data_size}"
3931 )));
3932 }
3933 }
3934
3935 Ok(())
3937 }
3938
3939 pub fn generate_backtrace_instruction(&mut self, depth: u8) -> Result<()> {
3944 info!("Generating Backtrace instruction: depth={}", depth);
3945
3946 let inst_buffer = self.reserve_instruction_region(
3948 (std::mem::size_of::<InstructionHeader>() + std::mem::size_of::<BacktraceData>())
3949 as u64,
3950 );
3951
3952 let inst_type_ptr = unsafe {
3954 self.builder
3955 .build_gep(
3956 self.context.i8_type(),
3957 inst_buffer,
3958 &[self.context.i32_type().const_int(
3959 std::mem::offset_of!(InstructionHeader, inst_type) as u64,
3960 false,
3961 )],
3962 "bt_inst_type_ptr",
3963 )
3964 .map_err(|e| CodeGenError::LLVMError(format!("Failed to get inst_type GEP: {e}")))?
3965 };
3966 let inst_type_val = self
3967 .context
3968 .i8_type()
3969 .const_int(InstructionType::Backtrace as u64, false);
3970 self.builder
3971 .build_store(inst_type_ptr, inst_type_val)
3972 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store inst_type: {e}")))?;
3973
3974 let data_length_ptr = unsafe {
3976 self.builder
3977 .build_gep(
3978 self.context.i8_type(),
3979 inst_buffer,
3980 &[self.context.i32_type().const_int(
3981 std::mem::offset_of!(InstructionHeader, data_length) as u64,
3982 false,
3983 )],
3984 "bt_data_length_ptr",
3985 )
3986 .map_err(|e| {
3987 CodeGenError::LLVMError(format!("Failed to get data_length GEP: {e}"))
3988 })?
3989 };
3990 let data_length_i16_ptr = self
3991 .builder
3992 .build_pointer_cast(
3993 data_length_ptr,
3994 self.context.ptr_type(AddressSpace::default()),
3995 "bt_data_length_i16_ptr",
3996 )
3997 .map_err(|e| CodeGenError::LLVMError(format!("Failed to cast data_length ptr: {e}")))?;
3998 let dl_val = self
3999 .context
4000 .i16_type()
4001 .const_int(std::mem::size_of::<BacktraceData>() as u64, false);
4002 self.builder
4003 .build_store(data_length_i16_ptr, dl_val)
4004 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store data_length: {e}")))?;
4005
4006 Ok(())
4008 }
4009
4010 fn resolve_variable_value(
4012 &mut self,
4013 var_name: &str,
4014 type_encoding: TypeKind,
4015 ) -> Result<BasicValueEnum<'ctx>> {
4016 info!(
4017 "Resolving variable value: {} ({:?})",
4018 var_name, type_encoding
4019 );
4020
4021 if self.variable_exists(var_name) {
4023 info!("Found script variable for '{}', loading value", var_name);
4024 return self.load_variable(var_name);
4025 }
4026
4027 match self.query_dwarf_for_variable(var_name)? {
4029 Some(var_info) => {
4030 info!(
4031 "Found DWARF variable: {} = {:?}",
4032 var_name, var_info.evaluation_result
4033 );
4034
4035 let dwarf_type = var_info.dwarf_type.as_ref().ok_or_else(|| {
4037 CodeGenError::DwarfError(format!(
4038 "Variable '{var_name}' has no type information in DWARF"
4039 ))
4040 })?;
4041
4042 let compile_context = self.get_compile_time_context()?;
4043 self.evaluate_result_to_llvm_value(
4044 &var_info.evaluation_result,
4045 dwarf_type,
4046 var_name,
4047 compile_context.pc_address,
4048 )
4049 }
4050 None => {
4051 let compile_context = self.get_compile_time_context()?;
4052 warn!(
4053 "Variable '{}' not found in DWARF at address 0x{:x}",
4054 var_name, compile_context.pc_address
4055 );
4056 Err(CodeGenError::VariableNotFound(var_name.to_string()))
4057 }
4058 }
4059 }
4060
4061 fn generate_print_complex_variable_runtime(
4063 &mut self,
4064 meta: PrintVarRuntimeMeta,
4065 eval_result: &ghostscope_dwarf::EvaluationResult,
4066 dwarf_type: &ghostscope_dwarf::TypeInfo,
4067 module_hint: Option<&str>,
4068 ) -> Result<()> {
4069 tracing::trace!(
4070 var_name_index = meta.var_name_index,
4071 type_index = meta.type_index,
4072 access_path = %meta.access_path,
4073 type_size = dwarf_type.size(),
4074 data_len_limit = meta.data_len_limit,
4075 eval = ?eval_result,
4076 "generate_print_complex_variable_runtime: begin"
4077 );
4078 let access_path_bytes = meta.access_path.as_bytes();
4082 let access_path_len = std::cmp::min(access_path_bytes.len(), 255); let type_size = dwarf_type.size() as usize;
4084 let mut data_len = std::cmp::min(type_size, meta.data_len_limit);
4085 if data_len > u16::MAX as usize {
4086 data_len = u16::MAX as usize;
4087 }
4088
4089 let header_size = std::mem::size_of::<InstructionHeader>();
4090 let data_struct_size = std::mem::size_of::<PrintComplexVariableData>();
4091 let reserved_payload = std::cmp::max(data_len, 12);
4093 let total_data_length = data_struct_size + access_path_len + reserved_payload;
4094 let total_size = header_size + total_data_length;
4095 tracing::trace!(
4096 header_size,
4097 data_struct_size,
4098 access_path_len,
4099 data_len,
4100 total_data_length,
4101 total_size,
4102 "generate_print_complex_variable_runtime: sizes computed"
4103 );
4104
4105 let inst_buffer = self.reserve_instruction_region(total_size as u64);
4107
4108 let inst_type_val = self
4112 .context
4113 .i8_type()
4114 .const_int(InstructionType::PrintComplexVariable as u64, false);
4115 self.builder
4116 .build_store(inst_buffer, inst_type_val)
4117 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store inst_type: {e}")))?;
4118 tracing::trace!(
4119 "generate_print_complex_variable_runtime: wrote inst_type=PrintComplexVariable"
4120 );
4121
4122 let data_length_ptr = unsafe {
4125 self.builder
4126 .build_gep(
4127 self.context.i8_type(),
4128 inst_buffer,
4129 &[self.context.i32_type().const_int(1, false)],
4130 "data_length_ptr",
4131 )
4132 .map_err(|e| {
4133 CodeGenError::LLVMError(format!("Failed to get data_length GEP: {e}"))
4134 })?
4135 };
4136 let data_length_ptr_cast = self
4137 .builder
4138 .build_pointer_cast(
4139 data_length_ptr,
4140 self.context.ptr_type(AddressSpace::default()),
4141 "data_length_ptr_cast",
4142 )
4143 .map_err(|e| CodeGenError::LLVMError(format!("Failed to cast data_length ptr: {e}")))?;
4144 self.builder
4145 .build_store(
4146 data_length_ptr_cast,
4147 self.context
4148 .i16_type()
4149 .const_int(total_data_length as u64, false),
4150 )
4151 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store data_length: {e}")))?;
4152 tracing::trace!(
4153 data_length = total_data_length,
4154 "generate_print_complex_variable_runtime: wrote data_length"
4155 );
4156
4157 let data_ptr = unsafe {
4159 self.builder
4160 .build_gep(
4161 self.context.i8_type(),
4162 inst_buffer,
4163 &[self.context.i32_type().const_int(header_size as u64, false)],
4164 "data_ptr",
4165 )
4166 .map_err(|e| CodeGenError::LLVMError(format!("Failed to get data GEP: {e}")))?
4167 };
4168
4169 let var_name_index_val = self
4171 .context
4172 .i16_type()
4173 .const_int(meta.var_name_index as u64, false);
4174 let var_name_index_off =
4176 std::mem::offset_of!(PrintComplexVariableData, var_name_index) as u64;
4177 let var_name_index_ptr_i8 = unsafe {
4178 self.builder
4179 .build_gep(
4180 self.context.i8_type(),
4181 data_ptr,
4182 &[self.context.i32_type().const_int(var_name_index_off, false)],
4183 "var_name_index_ptr_i8",
4184 )
4185 .map_err(|e| {
4186 CodeGenError::LLVMError(format!("Failed to get var_name_index GEP: {e}"))
4187 })?
4188 };
4189 let var_name_index_ptr_i16 = self
4190 .builder
4191 .build_pointer_cast(
4192 var_name_index_ptr_i8,
4193 self.context.ptr_type(AddressSpace::default()),
4194 "var_name_index_ptr_i16",
4195 )
4196 .map_err(|e| {
4197 CodeGenError::LLVMError(format!("Failed to cast var_name_index ptr: {e}"))
4198 })?;
4199 self.builder
4200 .build_store(var_name_index_ptr_i16, var_name_index_val)
4201 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store var_name_index: {e}")))?;
4202 tracing::trace!(
4203 var_name_index = meta.var_name_index,
4204 "generate_print_complex_variable_runtime: wrote var_name_index"
4205 );
4206
4207 let type_index_offset = std::mem::offset_of!(PrintComplexVariableData, type_index) as u64;
4210 let type_index_ptr_i8 = unsafe {
4211 self.builder
4212 .build_gep(
4213 self.context.i8_type(),
4214 data_ptr,
4215 &[self.context.i32_type().const_int(type_index_offset, false)],
4216 "type_index_ptr_i8",
4217 )
4218 .map_err(|e| {
4219 CodeGenError::LLVMError(format!("Failed to get type_index GEP: {e}"))
4220 })?
4221 };
4222 let type_index_ptr = self
4223 .builder
4224 .build_pointer_cast(
4225 type_index_ptr_i8,
4226 self.context.ptr_type(AddressSpace::default()),
4227 "type_index_ptr_i16",
4228 )
4229 .map_err(|e| CodeGenError::LLVMError(format!("Failed to cast type_index ptr: {e}")))?;
4230 let type_index_val = self
4231 .context
4232 .i16_type()
4233 .const_int(meta.type_index as u64, false);
4234 self.builder
4235 .build_store(type_index_ptr, type_index_val)
4236 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store type_index: {e}")))?;
4237 tracing::trace!(
4238 type_index = meta.type_index,
4239 "generate_print_complex_variable_runtime: wrote type_index"
4240 );
4241
4242 let access_path_len_off =
4245 std::mem::offset_of!(PrintComplexVariableData, access_path_len) as u64;
4246 let access_path_len_ptr = unsafe {
4247 self.builder
4248 .build_gep(
4249 self.context.i8_type(),
4250 data_ptr,
4251 &[self
4252 .context
4253 .i32_type()
4254 .const_int(access_path_len_off, false)],
4255 "access_path_len_ptr",
4256 )
4257 .map_err(|e| {
4258 CodeGenError::LLVMError(format!("Failed to get access_path_len GEP: {e}"))
4259 })?
4260 };
4261 self.builder
4262 .build_store(
4263 access_path_len_ptr,
4264 self.context
4265 .i8_type()
4266 .const_int(access_path_len as u64, false),
4267 )
4268 .map_err(|e| {
4269 CodeGenError::LLVMError(format!("Failed to store access_path_len: {e}"))
4270 })?;
4271 tracing::trace!(
4272 access_path_len,
4273 "generate_print_complex_variable_runtime: wrote access_path_len"
4274 );
4275
4276 let status_off = std::mem::offset_of!(PrintComplexVariableData, status) as u64;
4278 let status_ptr = unsafe {
4279 self.builder
4280 .build_gep(
4281 self.context.i8_type(),
4282 data_ptr,
4283 &[self.context.i32_type().const_int(status_off, false)],
4284 "status_ptr",
4285 )
4286 .map_err(|e| CodeGenError::LLVMError(format!("Failed to get status GEP: {e}")))?
4287 };
4288 self.builder
4289 .build_store(
4290 status_ptr,
4291 self.context
4292 .i8_type()
4293 .const_int(VariableStatus::Ok as u64, false),
4294 )
4295 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store status: {e}")))?;
4296
4297 let data_len_off = std::mem::offset_of!(PrintComplexVariableData, data_len) as u64;
4301 let data_len_ptr = unsafe {
4302 self.builder
4303 .build_gep(
4304 self.context.i8_type(),
4305 data_ptr,
4306 &[self.context.i32_type().const_int(data_len_off, false)],
4307 "data_len_ptr",
4308 )
4309 .map_err(|e| CodeGenError::LLVMError(format!("Failed to get data_len GEP: {e}")))?
4310 };
4311 let data_len_ptr_cast = self
4312 .builder
4313 .build_pointer_cast(
4314 data_len_ptr,
4315 self.context.ptr_type(AddressSpace::default()),
4316 "data_len_ptr_i16",
4317 )
4318 .map_err(|e| CodeGenError::LLVMError(format!("Failed to cast data_len ptr: {e}")))?;
4319 self.builder
4320 .build_store(
4321 data_len_ptr_cast,
4322 self.context.i16_type().const_int(data_len as u64, false),
4323 )
4324 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store data_len: {e}")))?;
4325 tracing::trace!(
4326 data_len,
4327 "generate_print_complex_variable_runtime: wrote data_len"
4328 );
4329
4330 let access_path_ptr = unsafe {
4334 self.builder
4335 .build_gep(
4336 self.context.i8_type(),
4337 data_ptr,
4338 &[self.context.i32_type().const_int(
4339 std::mem::size_of::<PrintComplexVariableData>() as u64,
4340 false,
4341 )],
4342 "access_path_ptr",
4343 )
4344 .map_err(|e| {
4345 CodeGenError::LLVMError(format!("Failed to get access_path GEP: {e}"))
4346 })?
4347 };
4348
4349 for (i, &byte) in access_path_bytes.iter().enumerate().take(access_path_len) {
4351 let byte_ptr = unsafe {
4352 self.builder
4353 .build_gep(
4354 self.context.i8_type(),
4355 access_path_ptr,
4356 &[self.context.i32_type().const_int(i as u64, false)],
4357 &format!("access_path_byte_{i}"),
4358 )
4359 .map_err(|e| {
4360 CodeGenError::LLVMError(format!("Failed to get access_path byte GEP: {e}"))
4361 })?
4362 };
4363 let byte_val = self.context.i8_type().const_int(byte as u64, false);
4364 self.builder.build_store(byte_ptr, byte_val).map_err(|e| {
4365 CodeGenError::LLVMError(format!("Failed to store access_path byte: {e}"))
4366 })?;
4367 }
4368 if access_path_len > 0 {
4369 tracing::trace!("generate_print_complex_variable_runtime: wrote access_path bytes");
4370 }
4371
4372 let variable_data_ptr = unsafe {
4374 self.builder
4375 .build_gep(
4376 self.context.i8_type(),
4377 access_path_ptr,
4378 &[self
4379 .context
4380 .i32_type()
4381 .const_int(access_path_len as u64, false)],
4382 "variable_data_ptr",
4383 )
4384 .map_err(|e| {
4385 CodeGenError::LLVMError(format!("Failed to get variable_data GEP: {e}"))
4386 })?
4387 };
4388
4389 let src_addr = self.evaluation_result_to_address_with_hint(
4392 eval_result,
4393 Some(status_ptr),
4394 module_hint,
4395 )?;
4396 tracing::trace!(src_addr = %{src_addr}, "generate_print_complex_variable_runtime: computed src_addr");
4397
4398 let ptr_type = self.context.ptr_type(AddressSpace::default());
4400 let i32_type = self.context.i32_type();
4401 let i64_type = self.context.i64_type();
4402 let dst_ptr = self
4403 .builder
4404 .build_bit_cast(variable_data_ptr, ptr_type, "dst_ptr")
4405 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4406 let size_val = i32_type.const_int(data_len as u64, false);
4407 let src_ptr = self
4408 .builder
4409 .build_int_to_ptr(src_addr, ptr_type, "src_ptr")
4410 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4411 let offsets_found = self.load_offsets_found_flag()?;
4412 let current_block = self.builder.get_insert_block().unwrap();
4413 let current_fn = current_block.get_parent().unwrap();
4414 let cont_block = self.context.append_basic_block(current_fn, "after_read");
4415 let skip_block = self.context.append_basic_block(current_fn, "offsets_skip");
4416 let found_block = self.context.append_basic_block(current_fn, "offsets_found");
4417 self.builder
4418 .build_conditional_branch(offsets_found, found_block, skip_block)
4419 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4420 self.builder.position_at_end(skip_block);
4421 self.mark_any_fail()?;
4422 self.builder
4423 .build_store(data_len_ptr_cast, self.context.i16_type().const_zero())
4424 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4425 self.builder
4426 .build_unconditional_branch(cont_block)
4427 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4428 self.builder.position_at_end(found_block);
4429
4430 let zero64 = i64_type.const_zero();
4432 let is_null = self
4433 .builder
4434 .build_int_compare(inkwell::IntPredicate::EQ, src_addr, zero64, "is_null")
4435 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4436 let null_block = self.context.append_basic_block(current_fn, "null_deref");
4437 let read_block = self.context.append_basic_block(current_fn, "read_user");
4438 self.builder
4439 .build_conditional_branch(is_null, null_block, read_block)
4440 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4441
4442 self.builder.position_at_end(null_block);
4444 self.builder
4445 .build_store(
4446 status_ptr,
4447 self.context
4448 .i8_type()
4449 .const_int(VariableStatus::NullDeref as u64, false),
4450 )
4451 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4452 self.builder
4454 .build_store(data_len_ptr_cast, self.context.i16_type().const_zero())
4455 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4456 self.mark_any_fail()?;
4458 self.builder
4459 .build_unconditional_branch(cont_block)
4460 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4461
4462 self.builder.position_at_end(read_block);
4464 let ret = self
4465 .create_bpf_helper_call(
4466 BPF_FUNC_probe_read_user as u64,
4467 &[dst_ptr, size_val.into(), src_ptr.into()],
4468 i32_type.into(),
4469 "probe_read_user",
4470 )?
4471 .into_int_value();
4472 let is_err = self
4473 .builder
4474 .build_int_compare(
4475 inkwell::IntPredicate::SLT,
4476 ret,
4477 i32_type.const_zero(),
4478 "ret_lt_zero",
4479 )
4480 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4481 let err_block = self.context.append_basic_block(current_fn, "read_err");
4482 let ok_block = self.context.append_basic_block(current_fn, "read_ok");
4483 self.builder
4484 .build_conditional_branch(is_err, err_block, ok_block)
4485 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4486
4487 self.builder.position_at_end(err_block);
4489 let cur_status1 = self
4491 .builder
4492 .build_load(self.context.i8_type(), status_ptr, "cur_status1")
4493 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4494 let is_ok1 = self
4495 .builder
4496 .build_int_compare(
4497 inkwell::IntPredicate::EQ,
4498 cur_status1.into_int_value(),
4499 self.context.i8_type().const_zero(),
4500 "status_is_ok1",
4501 )
4502 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4503 let readerr_val = self
4504 .context
4505 .i8_type()
4506 .const_int(VariableStatus::ReadError as u64, false)
4507 .into();
4508 let new_status1 = self
4509 .builder
4510 .build_select(is_ok1, readerr_val, cur_status1, "status_after_readerr")
4511 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4512 self.builder
4513 .build_store(status_ptr, new_status1)
4514 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4515 self.builder
4517 .build_store(
4518 data_len_ptr_cast,
4519 self.context.i16_type().const_int(12, false),
4520 )
4521 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4522 let errno_ptr = self
4524 .builder
4525 .build_pointer_cast(
4526 variable_data_ptr,
4527 self.context.ptr_type(AddressSpace::default()),
4528 "errno_ptr",
4529 )
4530 .map_err(|e| CodeGenError::LLVMError(format!("Failed to cast errno ptr: {e}")))?;
4531 self.builder
4532 .build_store(errno_ptr, ret)
4533 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store errno: {e}")))?;
4534 let addr_ptr_i8 = unsafe {
4536 self.builder
4537 .build_gep(
4538 self.context.i8_type(),
4539 variable_data_ptr,
4540 &[self.context.i32_type().const_int(4, false)],
4541 "addr_ptr_i8",
4542 )
4543 .map_err(|e| CodeGenError::LLVMError(format!("Failed to get addr GEP: {e}")))?
4544 };
4545 let addr_ptr = self
4546 .builder
4547 .build_pointer_cast(
4548 addr_ptr_i8,
4549 self.context.ptr_type(AddressSpace::default()),
4550 "addr_ptr",
4551 )
4552 .map_err(|e| CodeGenError::LLVMError(format!("Failed to cast addr ptr: {e}")))?;
4553 self.builder
4554 .build_store(addr_ptr, src_addr)
4555 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store addr: {e}")))?;
4556 self.mark_any_fail()?;
4558 self.builder
4559 .build_unconditional_branch(cont_block)
4560 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4561
4562 self.builder.position_at_end(ok_block);
4564 if data_len < dwarf_type.size() as usize {
4565 self.builder
4567 .build_store(
4568 status_ptr,
4569 self.context
4570 .i8_type()
4571 .const_int(VariableStatus::Truncated as u64, false),
4572 )
4573 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4574 self.mark_any_success()?;
4576 self.mark_any_fail()?;
4577 } else {
4578 self.mark_any_success()?;
4580 }
4581 self.builder
4582 .build_unconditional_branch(cont_block)
4583 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4584
4585 self.builder.position_at_end(cont_block);
4587
4588 Ok(())
4590 }
4591}
4592
4593#[cfg(test)]
4594mod tests {
4595 use super::*;
4596 use crate::CompileOptions;
4597
4598 #[test]
4599 fn computed_int_store_i64_compiles() {
4600 let context = inkwell::context::Context::create();
4601 let opts = CompileOptions::default();
4602 let mut ctx =
4603 EbpfContext::new(&context, "test_mod", Some(0), &opts).expect("create EbpfContext");
4604 let expr = crate::script::Expr::BinaryOp {
4606 left: Box::new(crate::script::Expr::Int(41)),
4607 op: crate::script::BinaryOp::Add,
4608 right: Box::new(crate::script::Expr::Int(1)),
4609 };
4610 let stmt =
4611 crate::script::Statement::Print(crate::script::PrintStatement::ComplexVariable(expr));
4612 let program = crate::script::Program::new();
4613 let res = ctx.compile_program(&program, "test_func", &[stmt], None, None, None);
4614 assert!(res.is_ok(), "Compilation failed: {:?}", res.err());
4615 }
4616
4617 #[test]
4618 fn computed_int_in_format_compiles() {
4619 let context = inkwell::context::Context::create();
4620 let opts = CompileOptions::default();
4621 let mut ctx =
4622 EbpfContext::new(&context, "test_mod", Some(0), &opts).expect("create EbpfContext");
4623 let expr = crate::script::Expr::BinaryOp {
4625 left: Box::new(crate::script::Expr::Int(1)),
4626 op: crate::script::BinaryOp::Add,
4627 right: Box::new(crate::script::Expr::Int(2)),
4628 };
4629 let stmt = crate::script::Statement::Print(crate::script::PrintStatement::Formatted {
4630 format: "sum:{}".to_string(),
4631 args: vec![expr],
4632 });
4633 let program = crate::script::Program::new();
4634 let res = ctx.compile_program(&program, "test_fmt", &[stmt], None, None, None);
4635 assert!(res.is_ok(), "Compilation failed: {:?}", res.err());
4636 }
4637
4638 #[test]
4639 fn memcmp_rejects_script_pointer_variable_now() {
4640 let context = inkwell::context::Context::create();
4641 let opts = CompileOptions::default();
4642 let mut ctx =
4643 EbpfContext::new(&context, "test_mod", Some(0), &opts).expect("create EbpfContext");
4644
4645 let decl = crate::script::Statement::VarDeclaration {
4647 name: "p".to_string(),
4648 value: crate::script::Expr::String("A".to_string()),
4649 };
4650
4651 let if_stmt = crate::script::Statement::If {
4653 condition: crate::script::Expr::BuiltinCall {
4654 name: "memcmp".to_string(),
4655 args: vec![
4656 crate::script::Expr::Variable("p".to_string()),
4657 crate::script::Expr::BuiltinCall {
4658 name: "hex".to_string(),
4659 args: vec![crate::script::Expr::String("41".to_string())],
4660 },
4661 crate::script::Expr::Int(1),
4662 ],
4663 },
4664 then_body: vec![crate::script::Statement::Print(
4665 crate::script::PrintStatement::String("OK".to_string()),
4666 )],
4667 else_body: None,
4668 };
4669
4670 let program = crate::script::Program::new();
4671 let res = ctx.compile_program(
4672 &program,
4673 "test_memcmp_ptr",
4674 &[decl, if_stmt],
4675 None,
4676 None,
4677 None,
4678 );
4679 assert!(
4680 res.is_err(),
4681 "Expected type error for script pointer variable in memcmp"
4682 );
4683 }
4684
4685 #[test]
4686 fn strncmp_requires_string_on_one_side_error_message() {
4687 let context = inkwell::context::Context::create();
4688 let opts = CompileOptions::default();
4689 let mut ctx = EbpfContext::new(&context, "test_mod", Some(0), &opts).expect("ctx");
4690
4691 let stmt = crate::script::Statement::If {
4693 condition: crate::script::Expr::BuiltinCall {
4694 name: "strncmp".to_string(),
4695 args: vec![
4696 crate::script::Expr::Int(42),
4697 crate::script::Expr::Int(43),
4698 crate::script::Expr::Int(2),
4699 ],
4700 },
4701 then_body: vec![crate::script::Statement::Print(
4702 crate::script::PrintStatement::String("OK".to_string()),
4703 )],
4704 else_body: None,
4705 };
4706 let program = crate::script::Program::new();
4707 let res = ctx.compile_program(&program, "test_strncmp_err", &[stmt], None, None, None);
4708 assert!(
4709 res.is_err(),
4710 "expected error when neither side is string (got {res:?})",
4711 );
4712 let msg = format!("{:?}", res.err());
4713 assert!(msg.contains("strncmp requires at least one string argument"));
4714 }
4715
4716 #[test]
4720 fn immutable_variable_redeclaration_rejected() {
4721 let context = inkwell::context::Context::create();
4722 let opts = CompileOptions::default();
4723 let mut ctx = EbpfContext::new(&context, "test_mod", Some(0), &opts).expect("ctx");
4724
4725 let d1 = crate::script::Statement::VarDeclaration {
4727 name: "x".to_string(),
4728 value: crate::script::Expr::Int(1),
4729 };
4730 let d2 = crate::script::Statement::VarDeclaration {
4731 name: "x".to_string(),
4732 value: crate::script::Expr::Int(2),
4733 };
4734 let program = crate::script::Program::new();
4735 let res = ctx.compile_program(&program, "immut", &[d1, d2], None, None, None);
4736 assert!(res.is_err(), "expected immutability error, got {res:?}");
4737 let msg = format!("{:?}", res.err());
4738 assert!(
4739 msg.contains("Redeclaration in the same scope") || msg.contains("immutable variable"),
4740 "unexpected error msg: {msg}"
4741 );
4742 }
4743
4744 #[test]
4745 fn immutable_alias_rebinding_rejected() {
4746 let context = inkwell::context::Context::create();
4747 let opts = CompileOptions::default();
4748 let mut ctx = EbpfContext::new(&context, "test_mod", Some(0), &opts).expect("ctx");
4749
4750 let a1 = crate::script::Statement::AliasDeclaration {
4752 name: "p".to_string(),
4753 target: crate::script::Expr::AddressOf(Box::new(crate::script::Expr::Variable(
4754 "arr".to_string(),
4755 ))),
4756 };
4757 let a2 = crate::script::Statement::AliasDeclaration {
4758 name: "p".to_string(),
4759 target: crate::script::Expr::AddressOf(Box::new(crate::script::Expr::Variable(
4760 "arr".to_string(),
4761 ))),
4762 };
4763 let program = crate::script::Program::new();
4764 let res = ctx.compile_program(&program, "immut_alias", &[a1, a2], None, None, None);
4765 assert!(
4766 res.is_err(),
4767 "expected immutability error for alias, got {res:?}"
4768 );
4769 }
4770
4771 #[test]
4772 fn alias_to_alias_with_const_offset_is_alias_variable() {
4773 let context = inkwell::context::Context::create();
4774 let opts = CompileOptions::default();
4775 let mut ctx = EbpfContext::new(&context, "test_mod", Some(0), &opts).expect("ctx");
4776 let s1 = crate::script::Statement::AliasDeclaration {
4778 name: "base".to_string(),
4779 target: crate::script::Expr::AddressOf(Box::new(crate::script::Expr::ArrayAccess(
4780 Box::new(crate::script::Expr::Variable("buf".to_string())),
4781 Box::new(crate::script::Expr::Int(0)),
4782 ))),
4783 };
4784 let s2 = crate::script::Statement::VarDeclaration {
4785 name: "tail".to_string(),
4786 value: crate::script::Expr::BinaryOp {
4787 left: Box::new(crate::script::Expr::Variable("base".to_string())),
4788 op: crate::script::BinaryOp::Add,
4789 right: Box::new(crate::script::Expr::Int(16)),
4790 },
4791 };
4792 let program = crate::script::Program::new();
4793 let res = ctx.compile_program(&program, "alias_stage", &[s1, s2], None, None, None);
4795 assert!(res.is_ok(), "expected alias-to-alias staging to compile");
4796 }
4797
4798 #[test]
4799 fn alias_to_alias_copy_is_alias_variable() {
4800 let context = inkwell::context::Context::create();
4801 let opts = CompileOptions::default();
4802 let mut ctx = EbpfContext::new(&context, "test_mod", Some(0), &opts).expect("ctx");
4803 let a = crate::script::Statement::AliasDeclaration {
4805 name: "a".to_string(),
4806 target: crate::script::Expr::AddressOf(Box::new(crate::script::Expr::MemberAccess(
4807 Box::new(crate::script::Expr::Variable("G_STATE".to_string())),
4808 "lib".to_string(),
4809 ))),
4810 };
4811 let b = crate::script::Statement::VarDeclaration {
4812 name: "b".to_string(),
4813 value: crate::script::Expr::Variable("a".to_string()),
4814 };
4815 let program = crate::script::Program::new();
4816 let res = ctx.compile_program(&program, "alias_copy", &[a, b], None, None, None);
4817 assert!(res.is_ok(), "expected alias-to-alias copy to compile");
4818 }
4819
4820 #[test]
4821 fn alias_self_reference_is_rejected_with_cycle_error() {
4822 let context = inkwell::context::Context::create();
4823 let opts = CompileOptions::default();
4824 let mut ctx = EbpfContext::new(&context, "test_mod", Some(0), &opts).expect("ctx");
4825
4826 let a = crate::script::Statement::AliasDeclaration {
4828 name: "a".to_string(),
4829 target: crate::script::Expr::AddressOf(Box::new(crate::script::Expr::Variable(
4830 "a".to_string(),
4831 ))),
4832 };
4833 let p = crate::script::Statement::Print(crate::script::PrintStatement::ComplexVariable(
4834 crate::script::Expr::Variable("a".to_string()),
4835 ));
4836 let program = crate::script::Program::new();
4837 let res = ctx.compile_program(&program, "alias_self", &[a, p], None, None, None);
4838 assert!(res.is_err(), "expected cycle error, got {res:?}");
4839 let msg = format!("{:?}", res.err());
4840 assert!(
4841 msg.contains("alias cycle") || msg.contains("depth exceeded"),
4842 "unexpected error: {msg}"
4843 );
4844 }
4845
4846 #[test]
4847 fn alias_mutual_cycle_is_rejected_with_cycle_error() {
4848 let context = inkwell::context::Context::create();
4849 let opts = CompileOptions::default();
4850 let mut ctx = EbpfContext::new(&context, "test_mod", Some(0), &opts).expect("ctx");
4851
4852 let a = crate::script::Statement::AliasDeclaration {
4854 name: "a".to_string(),
4855 target: crate::script::Expr::AddressOf(Box::new(crate::script::Expr::Variable(
4856 "b".to_string(),
4857 ))),
4858 };
4859 let b = crate::script::Statement::AliasDeclaration {
4860 name: "b".to_string(),
4861 target: crate::script::Expr::AddressOf(Box::new(crate::script::Expr::Variable(
4862 "a".to_string(),
4863 ))),
4864 };
4865 let p = crate::script::Statement::Print(crate::script::PrintStatement::ComplexVariable(
4866 crate::script::Expr::Variable("a".to_string()),
4867 ));
4868 let program = crate::script::Program::new();
4869 let res = ctx.compile_program(&program, "alias_cycle", &[a, b, p], None, None, None);
4870 assert!(res.is_err(), "expected cycle error, got {res:?}");
4871 let msg = format!("{:?}", res.err());
4872 assert!(
4873 msg.contains("alias cycle") || msg.contains("depth exceeded"),
4874 "unexpected error: {msg}"
4875 );
4876 }
4877
4878 #[test]
4879 fn strncmp_folds_with_script_string_and_literal_true() {
4880 let context = inkwell::context::Context::create();
4881 let opts = CompileOptions::default();
4882 let mut ctx = EbpfContext::new(&context, "test_mod", Some(0), &opts).expect("ctx");
4883
4884 let decl = crate::script::Statement::VarDeclaration {
4886 name: "s".to_string(),
4887 value: crate::script::Expr::String("ABC".to_string()),
4888 };
4889 let program = crate::script::Program::new();
4890 let res = ctx.compile_program(&program, "decl", &[decl], None, None, None);
4891 assert!(res.is_ok());
4892
4893 let expr = crate::script::Expr::BuiltinCall {
4895 name: "strncmp".to_string(),
4896 args: vec![
4897 crate::script::Expr::Variable("s".to_string()),
4898 crate::script::Expr::String("ABD".to_string()),
4899 crate::script::Expr::Int(2),
4900 ],
4901 };
4902 let v = ctx.compile_expr(&expr).expect("compile expr");
4903 match v {
4904 inkwell::values::BasicValueEnum::IntValue(iv) => {
4905 assert_eq!(iv.get_type().get_bit_width(), 1);
4906 let s = format!("{iv}");
4908 assert!(s.contains("i1 true") || s.contains("true"));
4909 }
4910 other => panic!("expected IntValue i1, got {other:?}"),
4911 }
4912 }
4913
4914 #[test]
4915 fn starts_with_folds_with_two_literals() {
4916 let context = inkwell::context::Context::create();
4917 let opts = CompileOptions::default();
4918 let mut ctx = EbpfContext::new(&context, "test_mod", Some(0), &opts).expect("ctx");
4919
4920 let expr = crate::script::Expr::BuiltinCall {
4922 name: "starts_with".to_string(),
4923 args: vec![
4924 crate::script::Expr::String("abcdef".to_string()),
4925 crate::script::Expr::String("abc".to_string()),
4926 ],
4927 };
4928 let v = ctx.compile_expr(&expr).expect("compile expr");
4929 match v {
4930 inkwell::values::BasicValueEnum::IntValue(iv) => {
4931 assert_eq!(iv.get_type().get_bit_width(), 1);
4932 let s = format!("{iv}");
4933 assert!(s.contains("i1 true") || s.contains("true"));
4934 }
4935 _ => panic!("expected i1"),
4936 }
4937 }
4938
4939 #[test]
4940 fn starts_with_requires_one_string_side_error() {
4941 let context = inkwell::context::Context::create();
4942 let opts = CompileOptions::default();
4943 let mut ctx = EbpfContext::new(&context, "test_mod", Some(0), &opts).expect("ctx");
4944
4945 let expr = crate::script::Expr::BuiltinCall {
4947 name: "starts_with".to_string(),
4948 args: vec![crate::script::Expr::Int(1), crate::script::Expr::Int(2)],
4949 };
4950 let res = ctx.compile_expr(&expr);
4951 assert!(res.is_err(), "expected error");
4952 let msg = format!("{:?}", res.err());
4953 assert!(msg.contains("starts_with requires at least one string argument"));
4954 }
4955
4956 #[test]
4957 fn shadowing_rejected_in_inner_scope() {
4958 let context = inkwell::context::Context::create();
4959 let opts = CompileOptions::default();
4960 let mut ctx = EbpfContext::new(&context, "test_mod", Some(0), &opts).expect("ctx");
4961
4962 let d1 = crate::script::Statement::VarDeclaration {
4964 name: "x".to_string(),
4965 value: crate::script::Expr::Int(1),
4966 };
4967 let inner =
4968 crate::script::Statement::Block(vec![crate::script::Statement::VarDeclaration {
4969 name: "x".to_string(),
4970 value: crate::script::Expr::Int(2),
4971 }]);
4972 let program = crate::script::Program::new();
4973 let res = ctx.compile_program(&program, "shadow", &[d1, inner], None, None, None);
4974 assert!(res.is_err(), "expected shadowing error");
4975 let msg = format!("{:?}", res.err());
4976 assert!(
4977 msg.contains("Shadowing is not allowed") || msg.contains("shadow"),
4978 "unexpected: {msg}"
4979 );
4980 }
4981
4982 #[test]
4983 fn out_of_scope_use_is_rejected() {
4984 let context = inkwell::context::Context::create();
4985 let opts = CompileOptions::default();
4986 let mut ctx = EbpfContext::new(&context, "test_mod", Some(0), &opts).expect("ctx");
4987
4988 let block =
4990 crate::script::Statement::Block(vec![crate::script::Statement::VarDeclaration {
4991 name: "y".to_string(),
4992 value: crate::script::Expr::Int(2),
4993 }]);
4994 let print_y = crate::script::Statement::Print(crate::script::PrintStatement::Variable(
4995 "y".to_string(),
4996 ));
4997 let program = crate::script::Program::new();
4998 let res = ctx.compile_program(
4999 &program,
5000 "out_of_scope",
5001 &[block, print_y],
5002 None,
5003 None,
5004 None,
5005 );
5006 assert!(
5007 res.is_err(),
5008 "expected out-of-scope or missing analyzer error"
5009 );
5010 }
5011
5012 #[test]
5013 fn memcmp_rejects_bare_integer_pointer_argument() {
5014 let context = inkwell::context::Context::create();
5015 let opts = CompileOptions::default();
5016 let mut ctx =
5017 EbpfContext::new(&context, "test_mod", Some(0), &opts).expect("create EbpfContext");
5018
5019 let decl = crate::script::Statement::VarDeclaration {
5021 name: "q".to_string(),
5022 value: crate::script::Expr::Int(0xdeadbeef),
5023 };
5024
5025 let if_stmt = crate::script::Statement::If {
5027 condition: crate::script::Expr::BuiltinCall {
5028 name: "memcmp".to_string(),
5029 args: vec![
5030 crate::script::Expr::Variable("q".to_string()),
5031 crate::script::Expr::BuiltinCall {
5032 name: "hex".to_string(),
5033 args: vec![crate::script::Expr::String("00".to_string())],
5034 },
5035 crate::script::Expr::Int(1),
5036 ],
5037 },
5038 then_body: vec![crate::script::Statement::Print(
5039 crate::script::PrintStatement::String("X".to_string()),
5040 )],
5041 else_body: None,
5042 };
5043
5044 let program = crate::script::Program::new();
5045 let res = ctx.compile_program(
5046 &program,
5047 "test_memcmp_int_ptr",
5048 &[decl, if_stmt],
5049 None,
5050 None,
5051 None,
5052 );
5053 assert!(res.is_err(), "Expected compilation error but got Ok");
5054 }
5055
5056 #[test]
5057 fn expr_to_name_truncates_utf8_safely() {
5058 let context = inkwell::context::Context::create();
5059 let opts = CompileOptions::default();
5060 let ctx = EbpfContext::new(&context, "test_mod", Some(0), &opts).expect("create ctx");
5061 let mut chain: Vec<String> = Vec::new();
5063 for _ in 0..50 {
5064 chain.push("错误".to_string());
5066 }
5067 let expr = crate::script::Expr::ChainAccess(chain);
5068 let s = ctx.expr_to_name(&expr);
5069 assert!(s.ends_with("..."));
5071 assert!(s.chars().count() <= 96);
5072 }
5073
5074 #[test]
5075 fn pointer_int_arithmetic_is_rejected_with_friendly_error() {
5076 let context = inkwell::context::Context::create();
5077 let opts = CompileOptions::default();
5078 let mut ctx = EbpfContext::new(&context, "ptr_arith", Some(0), &opts).expect("ctx");
5079 ctx.create_basic_ebpf_function("f").expect("fn");
5080
5081 let ptr_ty = ctx.context.ptr_type(inkwell::AddressSpace::default());
5083 let null_ptr = ptr_ty.const_null();
5084 ctx.store_variable("p", null_ptr.into()).expect("store ptr");
5085
5086 let expr = crate::script::Expr::BinaryOp {
5088 left: Box::new(crate::script::Expr::Variable("p".to_string())),
5089 op: crate::script::BinaryOp::Add,
5090 right: Box::new(crate::script::Expr::Int(1)),
5091 };
5092 let res = ctx.compile_expr(&expr);
5093 assert!(res.is_err(), "expected pointer-int arithmetic error");
5094 let msg = format!("{:?}", res.err());
5095 assert!(
5096 msg.contains("pointer and integer")
5097 || msg.contains("Unsupported operation between pointer and integer"),
5098 "unexpected error message: {msg}"
5099 );
5100 }
5101}