ghostscope_compiler/ebpf/
codegen.rs

1//! Code generation for instructions
2//!
3//! This module handles the conversion from statements to compiled instructions
4//! and generates LLVM IR for individual instructions.
5
6use 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/// Parameters for generating a PrintComplexVariable with runtime read
20#[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/// Source for complex formatted argument data
29#[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    /// Memory dump from a pointer/byte address with a static length
37    MemDump {
38        src_addr: inkwell::values::IntValue<'ctx>,
39        len: usize,
40    },
41    /// Memory dump with dynamic runtime length; bytes read up to min(len_value, max_len)
42    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    // Newly added: a value computed in LLVM at runtime (e.g., expression result)
55    ComputedInt {
56        value: inkwell::values::IntValue<'ctx>,
57        byte_len: usize, // typically 8
58    },
59}
60
61/// Argument descriptor for PrintComplexFormat
62#[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    /// Unified expression resolver: returns a ComplexArg carrying
73    /// a consistent var_name_index/type_index/access_path/data_len/source
74    /// with strict priority: script variables -> DWARF (locals/params/globals).
75    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            // 0) Alias variables: resolve to address and render as pointer value
79            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            // 1) Script variables first
95            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 this is a string variable, print its contents instead of address
99                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                        // Build a char[] type with length=bytes.len()
106                        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                        // Preserve signedness for display: map bit width to I8/I16/I32/I64
128                        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                        // Non-string pointer variable: print as address (hex)
153                        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            // 2) String literal -> Immediate bytes (for formatted args)
175            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            // 3) Integer literal -> Immediate i64 bytes
200            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            // 4) AddressOf: return AddressValue (pointer payload will be produced)
220            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            // 5) Complex lvalue shapes -> DWARF runtime read
247            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                // Previously clamped to 1993 bytes; now use full DWARF size (transport clamps per event size)
277                // data_len unchanged
278                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            // 6) Variable not in script scope → DWARF variable or computed fast-path for simple scalars
293            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 DWARF reports optimized-out at this PC, emit OptimizedOut type with no data
297                        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                            // Prefer computed value to avoid runtime reads
322                            let compiled = self.compile_expr(expr)?;
323                            match compiled {
324                                BasicValueEnum::IntValue(iv) => {
325                                    // Respect DWARF pointer types to keep pointer formatting
326                                    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                                    // Pointer register-backed → cast to i64 with pointer typeindex
360                                    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                                    // Fall back to runtime read path
380                                    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                            // Complex types or link-time addresses: use RuntimeRead
402                            // (globals/statics need memory read; not an address print unless AddressOf)
403                            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            // 7) Pointer arithmetic (ptr +/- K) → typed runtime read at computed address
433            E::BinaryOp { left, op, right } => {
434                use crate::script::ast::BinaryOp as BO;
435                // Support: ptr + int, int + ptr, ptr - int (int may be negative)
436                // Only allow when ptr side resolves to DWARF pointer/array; the offset must be an integer literal for now.
437                // We emit a RuntimeRead with computed location, preserving the pointed-to DWARF type.
438                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                        // Fallback to generic expression handling below
444                        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                // Try DWARF resolution for the pointer side
479                if let Some(var) = self.query_dwarf_for_complex_expr(ptr_side)? {
480                    if var.dwarf_type.is_some() {
481                        // Determine pointed-to/element type and compute location with scaled offset
482                        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                            // Fallback for unsized/void targets: print computed address as pointer
489                            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                // If pointer side cannot be resolved as DWARF pointer/array, fall back to computed int
523                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            // Binary and other rvalue expressions → compile to computed int
557            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    /// Emit a single PrintComplexVariable or a single-arg PrintComplexFormat depending on the arg source.
594    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                // Use ComplexFormat with "{}" to render address/immediate nicely
626                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                // Use ComplexFormat with "{}"; generate_print_complex_format_instruction handles MemDump
632                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    /// Generate PrintComplexVariable instruction that embeds a computed integer value (no runtime read)
639    /// This is used for `print expr;` where expr is an rvalue computed in eBPF.
640    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        // Build sizes
648        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; // computed expr has no access path
651        let total_data_length = data_struct_size + access_path_len + byte_len;
652        let total_size = header_size + total_data_length;
653
654        // Reserve space directly in the per-CPU accumulation buffer
655        let inst_buffer = self.reserve_instruction_region(total_size as u64);
656
657        // Write InstructionHeader.inst_type
658        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        // Write data_length (u16) at offset 1
667        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        // Data pointer (after header)
697        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        // var_name_index (u16)
709        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        // type_index (u16)
742        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        // access_path_len (u8) = 0
769        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        // status (u8) = 0
793        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        // data_len (u16)
809        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        // variable data starts right after PrintComplexVariableData (no access path)
836        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        // Store computed integer value into payload according to byte_len
851        match byte_len {
852            1 => {
853                let bitw = value.get_type().get_bit_width();
854                let v = if bitw == 1 {
855                    // Booleans must serialize as 0/1
856                    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                // Fallback: write lowest n bytes little-endian
937                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        // Already accumulated; EndInstruction will send the whole event
976        Ok(())
977    }
978    /// Determine if a TypeInfo qualifies as a "simple variable" for PrintVariableIndex
979    /// Simple: base types (bool/int/float/char), enums (with base type 1/2/4/8), pointers;
980    /// Complex: arrays, structs, unions, functions
981    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    /// Compute read size for a given DWARF type.
1001    /// No fallback: if DWARF doesn't provide size for arrays, return 0 and let caller error out.
1002    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                // Prefer DWARF-provided total size
1011                if let Some(ts) = total_size {
1012                    return *ts as usize;
1013                }
1014                // Fallback for arrays without total_size: need element_count * elem_size
1015                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    // (No implicit char[] fallback here; rely on DWARF/type resolver to provide sizes.)
1035
1036    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            // Keep space for ellipsis
1079            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    /// Heuristic to decide if an expression should be bound as a DWARF alias variable.
1095    /// Prefer shapes that resolve to a runtime address via DWARF or address-of:
1096    /// - AddressOf(...)
1097    /// - Member/Array/PointerDeref/Chain access
1098    /// - Variable that is a DWARF-backed symbol (not a script var)
1099    /// - Simple constant-offset on top of an aliasy expression: alias + K (K >= 0)
1100    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            // Alias variable names are alias candidates
1105            E::Variable(name) if self.alias_variable_exists(name) => true,
1106            // Explicit address-of is always an alias
1107            E::AddressOf(_) => true,
1108            // Constant offset on top of an alias-eligible expression
1109            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            // Otherwise, probe DWARF type: any DWARF-backed expression (pointer/array/struct/union/enum)
1119            // is treated as an alias so it can be used as a reusable base for member/index/address-of.
1120            other => matches!(self.query_dwarf_for_complex_expr(other), Ok(Some(_))),
1121        }
1122    }
1123
1124    // removed old helpers (pure lvalue/binary_op detection) — unified resolver handles shapes
1125
1126    /// Main entry point: compile program with staged transmission system
1127    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        // Step 1: Send TraceEventHeader
1135        self.send_trace_event_header()?;
1136        info!("Sent TraceEventHeader");
1137
1138        // Step 2: Send TraceEventMessage with dynamic trace_id
1139        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        // Reset per-event execution status flags
1144        self.store_flag_value("_gs_any_fail", 0)?;
1145        self.store_flag_value("_gs_any_success", 0)?;
1146
1147        // Step 3: Process each statement and generate LLVM IR on-demand
1148        let mut instruction_count = 0u16;
1149        for statement in &program.statements {
1150            instruction_count += self.compile_statement(statement)?;
1151        }
1152
1153        // Step 4: Send EndInstruction to mark completion
1154        self.send_end_instruction(instruction_count)?;
1155        info!(
1156            "Sent EndInstruction with {} total instructions",
1157            instruction_count
1158        );
1159
1160        // Step 5: Return the trace context for user-space parsing
1161        Ok(self.trace_context.clone())
1162    }
1163
1164    /// Compile a statement and return the number of instructions generated
1165    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                // Declare in current scope (no redeclaration or shadowing)
1172                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                // Declare in current scope (no redeclaration or shadowing)
1179                self.declare_name_in_current_scope(name)?;
1180                // Decide whether this is an alias binding (DWARF-backed address/reference)
1181                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                    // Compile the value expression and store as concrete variable
1187                    // Special-case: string literal and string var copy — record bytes for content printing
1188                    match value {
1189                        crate::script::Expr::String(s) => {
1190                            let mut bytes = s.as_bytes().to_vec();
1191                            bytes.push(0); // NUL terminate for display convenience
1192                            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                    // Disallow storing pointer values in script variables, except for string literals
1208                    if let BasicValueEnum::PointerValue(_) = compiled_value {
1209                        // Allow if RHS is a string literal OR a string variable (VarType::String)
1210                        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) // VarDeclaration doesn't generate instructions
1225                }
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                // Prepare condition context (runtime error capture)
1234                // Pretty expression text for warning
1235                let expr_text = self.expr_to_name(condition);
1236                let expr_index = self.trace_context.add_string(expr_text);
1237                // Activate condition context (compile-time flag) and reset runtime error byte
1238                self.condition_context_active = true;
1239                self.reset_condition_error()?;
1240
1241                // Compile condition expression
1242                let cond_value = self.compile_expr(condition)?;
1243
1244                // Convert condition to i1 (boolean) for branching
1245                let cond_bool = match cond_value {
1246                    BasicValueEnum::IntValue(int_val) => {
1247                        // Convert integer to boolean (non-zero = true)
1248                        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                // Get current function from builder
1267                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                // Create basic blocks for error/noerror and then/else paths
1275                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                // After cond compiled, deactivate compile-time flag
1291                self.condition_context_active = false;
1292
1293                // First branch: did runtime errors occur while evaluating the condition?
1294                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                // Error path: emit ExprError and decide destination
1302                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                // Also load failing address (i64)
1310                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                // Load flags
1317                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                // Decide where to go on error: if else_body is If (else-if), go to else_block to continue;
1325                // otherwise, skip else (suppress) and jump to merge.
1326                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                // No-error path: branch on boolean condition
1346                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                // Build then block
1354                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                // Build else block
1368                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                // Continue with merge block
1382                self.builder.position_at_end(merge_block);
1383
1384                // Return the maximum instructions from either branch
1385                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                // Start a new scope for the trace body
1399                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    /// Compile print statement and generate LLVM IR on-demand
1414    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                // 1. Add string to TraceContext
1421                let string_index = self.trace_context.add_string(s.to_string());
1422                // 2. Generate eBPF code for PrintStringIndex
1423                self.generate_print_string_index(string_index)?;
1424                Ok(1) // Generated 1 instruction
1425            }
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    /// Compile formatted print statement: collect all variable data and send as PrintComplexFormat instruction
1460    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        // Parse placeholders from the format string to support extended specifiers
1474        #[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; // arg cursor
1544        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                    // Force pointer address payload (u64) regardless of DWARF shape
1559                    let expr = &args[ai];
1560                    // Try compile to IntValue or PointerValue
1561                    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                    // Memory dump; handle static length at compile time. Other cases use default read and let user space trim.
1595                    // Handle star: consume length arg (as computed int) then value arg
1596                    let wants_ascii = matches!(conv, Conv::Ascii);
1597                    match lens {
1598                        LenSpec::Static(n) if ai < args.len() => {
1599                            // Resolve value expr address
1600                            let expr = &args[ai];
1601                            // Try get pointer address directly from expr value
1602                            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 compiled value is IntValue but DWARF type is a pointer, treat the IntValue as an address (pointer value)
1612                            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                                // Fallback: DWARF address (for arrays/char[N])
1630                                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                            // Dynamic length: consume length arg, then create a dynamic mem-dump for value
1669                            if ai + 1 >= args.len() {
1670                                break;
1671                            }
1672                            // length argument
1673                            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                            // value expression -> dynamic memdump with cap
1697                            let val_expr = &args[ai + 1];
1698                            // Resolve base address either from pointer-typed value or DWARF evaluation
1699                            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                            // Reserve up to configured per-arg cap for dynamic slices
1738                            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                            // Use script variable `name` as length; emit a length argument + a dynamic mem-dump argument
1768                            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                            // length as computed int
1777                            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                            // value
1808                            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                            // None: resolve value directly
1877                            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; // reserved for future per-arg metadata
1885                }
1886            }
1887        }
1888        self.generate_print_complex_format_instruction(format_string_index, &complex_args)?;
1889        Ok(1)
1890    }
1891
1892    /// Resolve variable with correct priority: script variables first, then DWARF variables
1893    /// This method is copied from protocol.rs to maintain functionality
1894    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        // Step 1: Check if it's a script-defined variable first
1898        if self.variable_exists(var_name) {
1899            info!("Found script variable: {}", var_name);
1900
1901            // Get the variable's LLVM value to infer type
1902            let loaded_value = self.load_variable(var_name)?;
1903            let type_encoding = self.infer_type_from_llvm_value(&loaded_value);
1904
1905            // Add to TraceContext
1906            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        // Step 2: If not found in script variables, try DWARF variables
1912        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        // Convert DWARF type information to TypeKind using existing method
1929        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        // Add to StringTable
1935        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    /// Synthesize a DWARF-like TypeInfo for a basic TypeKind (for script variables)
1946    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                // Use void* as a reasonable default for pointers/strings in script land
2010                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    /// Infer TypeKind from LLVM value type
2037    /// Copied from protocol.rs
2038    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, // Default to signed for script variables
2044                    16 => TypeKind::I16,
2045                    32 => TypeKind::I32,
2046                    64 => TypeKind::I64,
2047                    _ => TypeKind::I64, // Default fallback
2048                }
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, // Default fallback
2055                }
2056            }
2057            BasicValueEnum::PointerValue(_) => TypeKind::Pointer,
2058            _ => TypeKind::I64, // Conservative default
2059        }
2060    }
2061
2062    /// Generate eBPF code for PrintComplexFormat instruction with runtime reads for variables
2063    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        // Calculate total size with buffer-capacity awareness to avoid overflow
2072        // Instruction buffer capacity is currently 4096 (see create_instruction_buffer)
2073        const INSTR_BUF_CAP: usize = 4096;
2074        let fixed_overhead = std::mem::size_of::<InstructionHeader>()
2075            + std::mem::size_of::<PrintComplexFormatData>();
2076
2077        // First pass: accumulate header bytes and static payload, record dynamic args
2078        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(); // (arg_idx, max_len)
2082        let mut header_lens: Vec<usize> = Vec::with_capacity(complex_args.len());
2083        for (idx, a) in complex_args.iter().enumerate() {
2084            // Header bytes per-arg: var_name_index(2) + type_index(2) + access_path_len(1) + status(1) + data_len(2) + access_path
2085            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        // Available space for all argument payloads within the instruction buffer
2107        // Ensure we never exceed INSTR_BUF_CAP
2108        let mut remaining_for_payload = INSTR_BUF_CAP
2109            .saturating_sub(fixed_overhead)
2110            .saturating_sub(headers_total);
2111
2112        // Allocate static payload first
2113        remaining_for_payload = remaining_for_payload.saturating_sub(static_payload_total);
2114
2115        // Second pass: decide effective reserved payload for each arg
2116        // Default to computed static payload; dynamic args get clamped to remaining space
2117        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                    // find max_len for this dynamic arg and clamp to remaining
2127                    let (_, max_len) = dynamic_indices
2128                        .iter()
2129                        .copied()
2130                        .find(|(i, _)| *i == idx)
2131                        .unwrap_or((idx, 0));
2132                    // Ensure we always have space for error payload (errno+addr = 12 bytes) if possible
2133                    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        // Now compute final inst_data_size using effective reservations
2143        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        // Reserve buffer directly in accumulation buffer to avoid extra copy
2149        let buffer = self.reserve_instruction_region(total_size as u64);
2150
2151        // Avoid memset; global buffer is zero-initialized
2152
2153        // Write InstructionHeader
2154        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        // data_length at +1
2159        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        // Write PrintComplexFormatData at offset 4
2188        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        // format_string_index (u16) at +0
2202        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        // arg_count (u8) at +2
2218        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        // Start of variable payload after PrintComplexFormatData — use computed effective reservations
2236        let mut offset = std::mem::size_of::<PrintComplexFormatData>();
2237        for (arg_index, a) in complex_args.iter().enumerate() {
2238            // Per-arg reserved payload length
2239            let reserved_len = effective_reserved[arg_index];
2240
2241            // Base pointer = data_ptr + offset
2242            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            // var_name_index(u16) at +0
2256            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            // type_index(u16) at +2
2274            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            // status(u8) at +5
2302            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            // access_path_len(u8) at +4
2319            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            // access_path bytes at +6..+6+len
2339            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            // data_len(u16) at +6+path_len (store reserved_len to keep layout consistent)
2360            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            // variable data starts at +8+path_len
2391            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            // No dynamic cursor; we keep a compile-time offset and use reserved_len for layout
2408
2409            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                    // data_len already set to reserved_len
2436                }
2437                ComplexArgSource::MemDump { src_addr, len } => {
2438                    // Directly probe-read into payload to avoid byte-wise copies
2439                    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                    // Helper: long bpf_probe_read_user(void *dst, u32 size, const void *src)
2444                    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                    // Branch on ret == 0 and offsets available
2491                    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                    // ok: nothing extra to do
2513                    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                    // err: either offsets missing or helper failure
2518                    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                    // write errno + addr (12 bytes) to var_data_ptr; reserved sizing ensures this fits
2547                    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                    // Clamp runtime read to effective reserved length for this arg
2591                    let eff_max_len = effective_reserved[arg_index] as u32;
2592                    // Read up to rlen=min(len_value, max_len) into helper buffer, then copy bytes into payload
2593                    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                    // clamp negative to 0
2606                    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                    // Bound length by the reserved space (already ensures >= 12B when possible)
2623                    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                    // If effective length is zero, mark status and skip read.
2635                    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                    // Zero-length branch: set status=ZeroLength and continue.
2654                    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                    // Non-zero path: perform probe_read_user directly into var_data_ptr
2668                    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                    // ok: data already in var_data_ptr
2736                    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                    // err: status+errno+addr (clamped by reserved sizing)
2741                    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                    // Write computed integer into payload buffer based on requested byte_len
2809                    // Ensure the destination pointer element type matches the stored value type.
2810                    match *byte_len {
2811                        1 => {
2812                            let bitw = value.get_type().get_bit_width();
2813                            let v = if bitw == 1 {
2814                                // Bool: zero-extend to keep 0/1 in payload
2815                                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                                // wider than i8 -> truncate
2832                                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                                // exactly i8
2841                                *value
2842                            };
2843                            // var_data_ptr is i8* already; store directly
2844                            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                                // equal width: i16
2868                                *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                                // equal width: i32
2899                                *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                            // Fallback: write the lowest n bytes little-endian
2933                            // Truncate/extend to 64-bit, then emit byte stores
2934                            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                                // Extract byte i
2947                                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                    // Read from user memory at runtime via BPF helper
2984                    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                    // Compute source address; if link-time address, apply ASLR offsets via map
2993                    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                    // Offsets missing: record failure and continue without helper access.
3009                    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                    // Offsets found: proceed with null check and helper call.
3016                    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                    // status_ptr was stored in apl_ptr earlier (we named it status_ptr)
3023                    // Build NULL check
3024                    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                    // NULL path: status=1, keep reserved_len in header, no data write (buffer pre-zeroed)
3036                    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                    // Read path
3051                    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                    // Error branch: status=2 (read_user failed); write errno+addr payload at start; header keeps reserved_len
3076                    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                    // write errno at [0..4]
3086                    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                    // write addr at [4..12]
3100                    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                    // OK branch: success or truncated (header keeps reserved_len)
3134                    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                    // Compute address (apply ASLR if link-time address) and store as 8 bytes
3160                    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                    // header already set to reserved_len (8)
3177                }
3178            }
3179            // Advance compile-time offset by header_len + reserved_len
3180            offset += 2 + 2 + 1 + 1 + a.access_path.len() + 2 + reserved_len;
3181        }
3182
3183        // Already accumulated; EndInstruction will send the whole event
3184        Ok(())
3185    }
3186
3187    /// Generate eBPF code for PrintStringIndex instruction
3188    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        // Allocate instruction structure on eBPF stack
3195        // Reserve space in accumulation buffer for this instruction
3196        let inst_buffer = self.reserve_instruction_region(
3197            (std::mem::size_of::<InstructionHeader>() + std::mem::size_of::<PrintStringIndexData>())
3198                as u64,
3199        );
3200
3201        // Clear memory with static size
3202        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        // Avoid memset on eBPF; global buffer is zero-initialized and we write fields explicitly.
3209
3210        // Fill instruction header using byte offsets
3211        // inst_type at offset 0 (first field of InstructionHeader)
3212        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        // Fill string index data (after InstructionHeader)
3265        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        // Already accumulated; EndInstruction will send the whole event
3299        Ok(())
3300    }
3301
3302    /// Generate ExprError instruction with expression string index and error code/flags
3303    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        // Reserve space in accumulation buffer for this instruction
3311        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        // Store instruction type at offset 0
3318        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        // data_length
3327        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        // Payload fields after header
3359        // string_index at offset sizeof(InstructionHeader) + 0 (u16)
3360        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        // error_code at +2, flags at +3
3394        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        // Truncate/extend runtime error code to i8
3410        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        // Truncate/extend runtime flags to i8
3438        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        // failing_addr at +4 (u64)
3454        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        // Already accumulated; EndInstruction will send the whole event
3491        Ok(())
3492    }
3493
3494    /// Generate eBPF code for PrintVariableIndex instruction
3495    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        // Resolve type_index from DWARF if available; otherwise synthesize from TypeKind
3507        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                // Variable not found via DWARF; fall back to synthesized type info based on TypeKind
3514                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    /// Generate successful variable instruction with data
3530    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        // Determine data size based on type
3538        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, // Default to 8 bytes for complex types
3544        };
3545
3546        // Reserve space directly in per-CPU accumulation buffer
3547        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        // Avoid memset; global buffer is zero-initialized
3554
3555        // Store instruction type at offset 0
3556        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        // Store data_length field of InstructionHeader
3565        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        // Write PrintVariableIndexData after InstructionHeader
3598        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        // Store var_name_index using correct offset
3615        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        // Store type_encoding using correct offset
3649        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        // Store data_len using correct offset
3673        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); // Store as u16
3695        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        // Store type_index using correct offset
3700        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        // Store status (set to 0)
3729        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        // Store actual variable data after PrintVariableIndexData structure
3751        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        // Store the runtime variable value based on data size
3766        // The var_data contains the LLVM IR value (from register/memory access)
3767        match data_size {
3768            1 => {
3769                // Store as i8
3770                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                // Store as i16
3791                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                // Store as i32 or f32
3820                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                // Store as i64, f64, or pointer
3866                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                        // Store pointer as u64
3899                        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        // Already accumulated; EndInstruction will send the whole event
3936        Ok(())
3937    }
3938
3939    // PrintVariableError instruction has been removed; compile-time errors are returned as Err,
3940    // runtime errors are carried via per-variable status in Print* instructions.
3941
3942    /// Generate Backtrace instruction
3943    pub fn generate_backtrace_instruction(&mut self, depth: u8) -> Result<()> {
3944        info!("Generating Backtrace instruction: depth={}", depth);
3945
3946        // Reserve space directly for Backtrace instruction
3947        let inst_buffer = self.reserve_instruction_region(
3948            (std::mem::size_of::<InstructionHeader>() + std::mem::size_of::<BacktraceData>())
3949                as u64,
3950        );
3951
3952        // Write InstructionHeader.inst_type
3953        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        // Write InstructionHeader.data_length (u16)
3975        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        // Already accumulated; EndInstruction will send the whole event. Depth currently unused at BPF level.
4007        Ok(())
4008    }
4009
4010    /// Resolve variable value from script variables first, then DWARF
4011    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        // 1) Script variable first
4022        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        // 2) DWARF variable as fallback
4028        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                // Require DWARF type information
4036                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    /// Generate PrintComplexVariable instruction and copy data at runtime using probe_read_user
4062    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        // Compute sizes first, then reserve instruction region directly in accumulation buffer
4079
4080        // Compute sizes
4081        let access_path_bytes = meta.access_path.as_bytes();
4082        let access_path_len = std::cmp::min(access_path_bytes.len(), 255); // u8 max
4083        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        // Reserve enough space to hold either the value (read_len) or an error payload (12 bytes)
4092        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        // Reserve space now that sizes are known
4106        let inst_buffer = self.reserve_instruction_region(total_size as u64);
4107
4108        // Avoid memset; reserved map value bytes are zero-initialized
4109
4110        // Write InstructionHeader.inst_type at offset 0
4111        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        // Write InstructionHeader
4123        // data_length field (u16) at offset 1
4124        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        // Data pointer (after header)
4158        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        // var_name_index (u16)
4170        let var_name_index_val = self
4171            .context
4172            .i16_type()
4173            .const_int(meta.var_name_index as u64, false);
4174        // Store var_name_index at offset offsetof(PrintComplexVariableData, var_name_index)
4175        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        // type_index (u16) right after var_name_index
4208        // type_index at offset offsetof(PrintComplexVariableData, type_index) = 2
4209        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        // access_path_len (u8)
4243        // access_path_len at offset offsetof(..., access_path_len)
4244        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        // status (u8) at offset offsetof(..., status)
4277        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        // (Optimized-out handling moved below after data_len pointer is available)
4298
4299        // data_len (u16)
4300        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        // Optimized-out case is handled earlier by resolving to an OptimizedOut type and ImmediateBytes path.
4331
4332        // access_path bytes start after PrintComplexVariableData
4333        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        // Copy access path bytes
4350        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        // Variable data starts after access_path
4373        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        // Compute source address with ASLR-aware helper, honoring module hint
4390        // Prefer a previously recorded module path for offsets; fall back handled in helper
4391        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        // Setup common types and casts
4399        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        // Branch: NULL deref if src_addr == 0
4431        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        // NULL path
4443        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        // data_len = 0
4453        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        // mark fail
4457        self.mark_any_fail()?;
4458        self.builder
4459            .build_unconditional_branch(cont_block)
4460            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4461
4462        // Read path
4463        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        // Error: status=2 (read_user failed); attach errno+addr payload and set data_len=12
4488        self.builder.position_at_end(err_block);
4489        // Only set ReadError if status is still Ok (preserve OffsetsUnavailable etc.)
4490        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        // data_len = 12 (errno:i32 + addr:u64)
4516        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        // write errno at [0..4]
4523        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        // write addr at [4..12]
4535        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        // mark fail
4557        self.mark_any_fail()?;
4558        self.builder
4559            .build_unconditional_branch(cont_block)
4560            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
4561
4562        // OK path: status=0; optional truncated if data_len_limit < dwarf_type.size()
4563        self.builder.position_at_end(ok_block);
4564        if data_len < dwarf_type.size() as usize {
4565            // truncated
4566            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            // mark both success and fail
4575            self.mark_any_success()?;
4576            self.mark_any_fail()?;
4577        } else {
4578            // success
4579            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        // Continue
4586        self.builder.position_at_end(cont_block);
4587
4588        // Already accumulated; EndInstruction will send the whole event
4589        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        // print {} with a pure script integer expression triggers ComputedInt path
4605        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        // formatted print with expression argument should also route into ComputedInt path
4624        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 p = "A";  // script pointer to const string (no longer accepted as memcmp arg)
4646        let decl = crate::script::Statement::VarDeclaration {
4647            name: "p".to_string(),
4648            value: crate::script::Expr::String("A".to_string()),
4649        };
4650
4651        // if memcmp(p, hex("41"), 1) { print "OK"; }
4652        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        // strncmp(42, 43, 2) -> neither side is string (literal/var); expect type error
4692        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    // No test needed here for string var copy rejection; current semantics allow
4717    // let s = "A"; let p = s; as a string-to-string assignment.
4718
4719    #[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 x = 1; let x = 2;  (same trace block)
4726        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 p = &arr[0]; let p = &arr[0];
4751        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 base = &buf[0]; let tail = base + 16;
4777        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        // Should treat tail as alias (not as value), thus compile_program succeeds
4794        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 = &G_STATE.lib; let b = a;
4804        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 = &a; print a;
4827        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 = &b; let b = &a; print a;
4853        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        // Prepare: let s = "ABC";
4885        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        // Expression: strncmp(s, "ABD", 2) -> true
4894        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                // true expected (string repr may vary across LLVM versions, check both forms)
4907                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        // Expression: starts_with("abcdef", "abc") -> true
4921        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        // Neither side is string
4946        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 x = 1; { let x = 2; }
4963        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 y = 2; } print y;  -> y is out of scope
4989        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 q = 0xdeadbeef;  // integer, not a pointer value
5020        let decl = crate::script::Statement::VarDeclaration {
5021            name: "q".to_string(),
5022            value: crate::script::Expr::Int(0xdeadbeef),
5023        };
5024
5025        // if memcmp(q, hex("00"), 1) { print "X"; }
5026        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        // Build a long expression comprised of multibyte chars to exceed 96 chars
5062        let mut chain: Vec<String> = Vec::new();
5063        for _ in 0..50 {
5064            // each "错误" is 6 bytes, 2 chars -> quickly exceeds 96 chars
5065            chain.push("错误".to_string());
5066        }
5067        let expr = crate::script::Expr::ChainAccess(chain);
5068        let s = ctx.expr_to_name(&expr);
5069        // Ensure we got a trailing ellipsis and no panic on multibyte boundary
5070        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        // Create a script variable 'p' of pointer type (null pointer)
5082        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        // Expression: p + 1
5087        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}